Überblick
Die Frage wurde angesprochen. Diese Antwort fügt jedoch einige praktische Beispiele hinzu, um das grundlegende Verständnis von Datenklassen zu erleichtern.
Was genau sind Python-Datenklassen und wann ist es am besten, sie zu verwenden?
- Codegeneratoren : Boilerplate-Code generieren; Sie können spezielle Methoden in einer regulären Klasse implementieren oder von einer Datenklasse automatisch implementieren lassen.
- Datencontainer : Strukturen, die Daten enthalten (z. B. Tupel und Diktate), häufig mit gepunkteten Attributzugriffen wie Klassen
namedtuple
und anderen .
"veränderbare Namedtuples mit Standard [s]"
Folgendes bedeutet der letztere Satz:
- veränderlich : Standardmäßig können Datenklassenattribute neu zugewiesen werden. Sie können sie optional unveränderlich machen (siehe Beispiele unten).
- namedtuple : Sie haben einen gepunkteten Attributzugriff wie eine
namedtuple
oder eine reguläre Klasse.
- Standard : Sie können Attributen Standardwerte zuweisen.
Im Vergleich zu herkömmlichen Klassen sparen Sie in erster Linie bei der Eingabe von Boilerplate-Code.
Eigenschaften
Dies ist eine Übersicht über Datenklassenfunktionen (TL; DR? Siehe Übersichtstabelle im nächsten Abschnitt).
Was man bekommt
Hier sind Funktionen, die Sie standardmäßig von Datenklassen erhalten.
Attribute + Darstellung + Vergleich
import dataclasses
@dataclasses.dataclass
#@dataclasses.dataclass() # alternative
class Color:
r : int = 0
g : int = 0
b : int = 0
Diese Standardeinstellungen werden bereitgestellt, indem die folgenden Schlüsselwörter automatisch auf gesetzt werden True
:
@dataclasses.dataclass(init=True, repr=True, eq=True)
Was Sie einschalten können
Zusätzliche Funktionen sind verfügbar, wenn die entsprechenden Schlüsselwörter festgelegt sind True
.
Auftrag
@dataclasses.dataclass(order=True)
class Color:
r : int = 0
g : int = 0
b : int = 0
Die Bestellmethoden sind jetzt implementiert (Überladungsoperatoren :) < > <= >=
, ähnlich wie functools.total_ordering
bei stärkeren Gleichheitstests.
Hashable, veränderlich
@dataclasses.dataclass(unsafe_hash=True) # override base `__hash__`
class Color:
...
Obwohl das Objekt möglicherweise veränderlich (möglicherweise unerwünscht) ist, wird ein Hash implementiert.
Hashable, unveränderlich
@dataclasses.dataclass(frozen=True) # `eq=True` (default) to be immutable
class Color:
...
Ein Hash ist jetzt implementiert und das Ändern des Objekts oder das Zuweisen von Attributen ist nicht zulässig.
Insgesamt ist das Objekt hashbar, wenn entweder unsafe_hash=True
oder frozen=True
.
Siehe auch die ursprüngliche Hashing-Logik-Tabelle mit weiteren Details.
Was du nicht bekommst
Um die folgenden Funktionen zu erhalten, müssen spezielle Methoden manuell implementiert werden:
Auspacken
@dataclasses.dataclass
class Color:
r : int = 0
g : int = 0
b : int = 0
def __iter__(self):
yield from dataclasses.astuple(self)
Optimierung
@dataclasses.dataclass
class SlottedColor:
__slots__ = ["r", "b", "g"]
r : int
g : int
b : int
Die Objektgröße wird jetzt reduziert:
>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888
__slots__
Verbessert unter bestimmten Umständen auch die Geschwindigkeit beim Erstellen von Instanzen und beim Zugriff auf Attribute. Außerdem erlauben Slots keine Standardzuweisungen. Andernfalls wird a ValueError
angehoben.
Weitere Informationen zu Slots finden Sie in diesem Blogbeitrag .
Übersichtstabelle
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Feature | Keyword | Example | Implement in a Class |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes | init | Color().r -> 0 | __init__ |
| Representation | repr | Color() -> Color(r=0, g=0, b=0) | __repr__ |
| Comparision* | eq | Color() == Color(0, 0, 0) -> True | __eq__ |
| | | | |
| Order | order | sorted([Color(0, 50, 0), Color()]) -> ... | __lt__, __le__, __gt__, __ge__ |
| Hashable | unsafe_hash/frozen | {Color(), {Color()}} -> {Color(r=0, g=0, b=0)} | __hash__ |
| Immutable | frozen + eq | Color().r = 10 -> TypeError | __setattr__, __delattr__ |
| | | | |
| Unpacking+ | - | r, g, b = Color() | __iter__ |
| Optimization+ | - | sys.getsizeof(SlottedColor) -> 888 | __slots__ |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
+ Diese Methoden werden nicht automatisch generiert und erfordern eine manuelle Implementierung in eine Datenklasse.
* __ne__
wird nicht benötigt und ist daher nicht implementiert .
Zusatzfunktionen
Nachinitialisierung
@dataclasses.dataclass
class RGBA:
r : int = 0
g : int = 0
b : int = 0
a : float = 1.0
def __post_init__(self):
self.a : int = int(self.a * 255)
RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)
Erbe
@dataclasses.dataclass
class RGBA(Color):
a : int = 0
Konvertierungen
Konvertieren Sie eine Datenklasse rekursiv in ein Tupel oder ein Diktat :
>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}
Einschränkungen
Verweise
- R. Hettingers Vortrag über Datenklassen: Der Codegenerator zum Beenden aller Codegeneratoren
- T. Hunners Vortrag über einfachere Klassen: Python-Klassen ohne die ganze Cruft
- Pythons Dokumentation zu Hashing-Details
- Real Pythons Leitfaden zum ultimativen Leitfaden für Datenklassen in Python 3.7
- A. Shaws Blogbeitrag über Eine kurze Tour durch Python 3.7-Datenklassen
- E. Smiths Github-Repository für Datenklassen
namedtuple
s sind unveränderlich und können keine Standardwerte für die Attribute haben, während Datenklassen veränderbar sind und diese haben können.