Crea facilmente classi in Python con @dataclass

Jan 22 2023
In Python, il decoratore @dataclass semplifica il processo di creazione delle classi aggiungendo automaticamente metodi comuni e promuovendo le migliori pratiche per scrivere codice ben scritto e sicuro. È disponibile da Python 3.
Immagine generata da Jacob Ferus

In Python, il @dataclassdecoratore semplifica il processo di creazione delle classi aggiungendo automaticamente metodi comuni e promuovendo le migliori pratiche per scrivere codice ben scritto e sicuro. È disponibile da Python 3.7. In questo articolo vi parlerò di questo decoratore.

Iniziamo con un semplice esempio:

Definendo due variabili statiche con suggerimenti di tipo e aggiungendo il @dataclassdecoratore, otteniamo una classe con un __init__metodo corrispondente al seguente:

Sebbene saltare il __init__metodo sia utile, la dataclass offre molto di più di questo. Per capire cosa, diamo un'occhiata alle opzioni disponibili per il decoratore:

Ho omesso alcuni degli argomenti e mantenuto quelli che ritengo più importanti da discutere. Si noti che utilizzando:

comporterà in modo equivalente l'utilizzo degli argomenti predefiniti.

Come si può vedere, a seconda degli argomenti, i metodi verranno aggiunti implicitamente alla classe. Per impostazione predefinita:

  • init=True aggiungerà __init__come abbiamo visto sopra
  • repr=True aggiungerà __repr__, cioè ciò che viene mostrato durante la stampa
  • eq=True aggiungerà __eq__in base ai campi
  • order=True aggiungerà __lt__(), __le__(), __gt__()e__ge__()

Nota che ovviamente puoi anche definire altri metodi di classe personalizzati come faresti normalmente in una classe. Di seguito definisco una classe semplice senza il decoratore @dataclass per il confronto.

Stampa

Con repr=True, che è l'impostazione predefinita, l'output durante la stampa viene modificato. Confrontiamo cosa __repr__mostrano l'oggetto creato con la classe dati e l'oggetto creato con la classe normale (senza ) quando vengono stampati:

Come si può vedere con la classe normale, le informazioni stampate ci dicono molto poco sull'oggetto per impostazione predefinita. D'altra parte, la dataclass mostra sinteticamente i valori dell'oggetto, avendo sovrascritto il default __repr__-method.

Uguaglianza

Il comportamento predefinito dell'uguaglianza per le classi personalizzate può creare confusione. Se confronti una classe personalizzata senza un __eq__metodo definito, due oggetti saranno uguali solo se sono esattamente lo stesso oggetto:

Pertanto, i valori degli attributi non contano. Ma utilizzando la dataclass e eq=True(impostata per impostazione predefinita) viene aggiunta la forma di uguaglianza più prevista in cui gli attributi vengono confrontati (ovvero __eq__viene sovrascritto):

Ordine

Impostando il orderparametro su true (false per impostazione predefinita) e eq su true ( other=Truee eq=Falsecauserà la generazione di un errore) i metodi:

  • __lt__()
  • __le__()
  • __gt__()
  • __ge__()

Immutabilità e hashing

Infine, parliamo degli argomenti frozene unsafe_hash. Questi argomenti sono molto utili e facilitano la corretta gestione di hashing, uguaglianza e mutazione. Se non hai familiarità con l'hashing in Python, ti consiglio di leggere l'articolo: Understanding Hashing and Equality in Python with __hash__ and __eq__ .

L'impostazione frozen=Trueafferma che la classe deve essere di sola lettura o immutabile, ovvero dopo l'inizializzazione di un oggetto, nessun campo può essere modificato:

Le due proprietà frozene eqinfluenzeranno il metodo di hashing implicito che viene creato:

  • frozen=True, eq=True=> un __hash__metodo viene generato automaticamente utilizzando i campi
  • frozen=True, eq=False=> default __hash__-method, cioè non usare i campi
  • frozen=False, eq=True=> Unhashable, viene generato un errore in caso di hash
  • frozen=False, eq=False=> default __hash__-method, cioè non usare i campi

Ma perché la classe deve essere congelata? Se aggiungi un oggetto a un set o a un dizionario, l'hash generato viene utilizzato per posizionarlo all'interno della mappa hash. Successivamente, quando vuoi recuperare l'oggetto, usi lo stesso hash per trovarlo. Ora, se l'oggetto è stato modificato, dovrebbe cambiare anche l'hash (poiché è basato sui campi). Di conseguenza, non sarai più in grado di ritrovare l'oggetto. Pertanto, affinché l'hash rimanga costante, i campi su cui si basa devono rimanere costanti. Per questo motivo, if eq=Truee frozen=False, non è disponibile alcun metodo hash.

Che dire unsafe_hash? Come descritto, il comportamento predefinito della definizione implicita di __hash__si basa su principi che promuovono le migliori pratiche. Tuttavia, se desideri comunque avere un __hash__metodo definito comunque, forse a causa di circostanze speciali, puoi impostarlo unsafe_hash=Truee lo farà. Ma tieni presente che la parola chiave unsafe è lì per un motivo:

Conclusione

@dataclass in Python è un utile decoratore che ci consente di risparmiare tempo, seguire le migliori pratiche e semplificare il processo di creazione delle classi. Credo che sia adatto a un'ampia varietà di situazioni.

Grazie per aver letto!

Potrebbero interessarti anche: