Panoramica
La domanda è stata affrontata. Tuttavia, questa risposta aggiunge alcuni esempi pratici per aiutare nella comprensione di base degli occhiali.
Cosa sono esattamente le classi di dati Python e quando è meglio usarle?
- generatori di codice : genera codice boilerplate; puoi scegliere di implementare metodi speciali in una classe normale o fare in modo che una dataclass li implementi automaticamente.
- contenitori di dati : strutture che contengono dati (ad es. tuple e dadi), spesso con accesso ad attributi punteggiato, come classi
namedtuple
e altri .
"mutable namedtuples con default [s]"
Ecco cosa significa quest'ultima frase:
- mutabile : per impostazione predefinita, gli attributi della dataclass possono essere riassegnati. Facoltativamente, puoi renderli immutabili (vedi esempi di seguito).
- namedtuple : hai punteggiato, accedi agli attributi come una
namedtuple
o una classe normale.
- impostazione predefinita : è possibile assegnare valori predefiniti agli attributi.
Rispetto alle classi comuni, si risparmia principalmente sulla digitazione del codice del boilerplate.
Caratteristiche
Questa è una panoramica delle funzionalità della dataclass (TL; DR? Vedere la tabella riassuntiva nella sezione successiva).
Cosa ottieni
Ecco le funzionalità che si ottengono di default dai dataclass.
Attributi + Rappresentazione + Confronto
import dataclasses
@dataclasses.dataclass
#@dataclasses.dataclass() # alternative
class Color:
r : int = 0
g : int = 0
b : int = 0
Queste impostazioni predefinite vengono fornite impostando automaticamente le seguenti parole chiave su True
:
@dataclasses.dataclass(init=True, repr=True, eq=True)
Cosa puoi accendere
Funzionalità aggiuntive sono disponibili se le parole chiave appropriate sono impostate su True
.
Ordine
@dataclasses.dataclass(order=True)
class Color:
r : int = 0
g : int = 0
b : int = 0
I metodi di ordinazione sono ora implementati (operatori di sovraccarico:) < > <= >=
, in modo simile a functools.total_ordering
test di uguaglianza più forti.
Hashable, Mutable
@dataclasses.dataclass(unsafe_hash=True) # override base `__hash__`
class Color:
...
Sebbene l'oggetto sia potenzialmente mutabile (probabilmente indesiderato), viene implementato un hash.
Hashable, Immutable
@dataclasses.dataclass(frozen=True) # `eq=True` (default) to be immutable
class Color:
...
Ora è implementato un hash e non è possibile modificare l'oggetto o assegnare agli attributi.
Nel complesso, l'oggetto è hasash se uno unsafe_hash=True
o frozen=True
.
Vedi anche la tabella logica di hashing originale con maggiori dettagli.
Quello che non ottieni
Per ottenere le seguenti funzionalità, è necessario implementare manualmente metodi speciali:
Apertura della confezione
@dataclasses.dataclass
class Color:
r : int = 0
g : int = 0
b : int = 0
def __iter__(self):
yield from dataclasses.astuple(self)
Ottimizzazione
@dataclasses.dataclass
class SlottedColor:
__slots__ = ["r", "b", "g"]
r : int
g : int
b : int
La dimensione dell'oggetto è ora ridotta:
>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888
In alcune circostanze, __slots__
migliora anche la velocità di creazione di istanze e di accesso agli attributi. Inoltre, gli slot non consentono assegnazioni predefinite; in caso contrario, ValueError
viene sollevato a.
Vedi di più sulle slot in questo post del blog .
Tabella riassuntiva
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| 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__ |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
+ Questi metodi non vengono generati automaticamente e richiedono un'implementazione manuale in una classe di dati.
* __ne__
non è necessario e quindi non implementato .
Caratteristiche aggiuntive
Post-inizializzazione
@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)
Eredità
@dataclasses.dataclass
class RGBA(Color):
a : int = 0
conversioni
Convertire una classe di dati ad una tupla o un dizionario, in modo ricorsivo :
>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}
limitazioni
Riferimenti
- Discorso di R. Hettinger su Dataclasses: il generatore di codice per terminare tutti i generatori di codice
- Il discorso di T. Hunner su Classi più semplici: Classi Python senza tutte le cruft
- Documentazione di Python sui dettagli di hashing
- Guida di Real Python su The Ultimate Guide to Data Classes in Python 3.7
- Il post del blog di A. Shaw in Un breve tour delle classi di dati di Python 3.7
- Il repository github di E. Smith su dataclass
namedtuple
sono immutabili e non possono avere valori predefiniti per gli attributi, mentre le classi di dati sono mutabili e possono averli.