Sostieni AppuntiFacili con una piccola donazione su PayPal
Dona con PayPalLe @dataclass in Python automatizzano la creazione di alcuni metodi speciali per le classi, che altrimenti dovremmo definire manualmente.
Questo include metodi come __init__, __repr__, __eq__, semplificando notevolmente la gestione di classi che fungono principalmente da contenitori di dati.
Per utilizzarle, basta importare il decoratore @dataclass dal modulo dataclasses:
from dataclasses import dataclass
e applicarlo alla classe:
@dataclass
class NomeClasse:
...
Vediamo la differenza tra una classe normale e una @dataclass con un esempio concreto.
Senza usare la @dataclass:
class Point:
x: int
y: int
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point(x={self.x}, y={self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(1, 2)
p2 = Point(2, 1)
print(p1, p2)
print(p1 == p2)
Output:
Point(x=1, y=2) Point(x=2, y=1)
False
Come si può vedere, dobbiamo scrivere manualmente sia il costruttore (__init__), sia i metodi __repr__ e __eq__.
Usando la @dataclass:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
p1 = Point(1, 2)
p2 = Point(2, 1)
print(p1, p2)
print(p1 == p2)
Output:
Point(x=1, y=2) Point(x=2, y=1)
False
L’output è lo stesso, ma non è stato necessario definire a mano i metodi __init__, __repr__ o __eq__.
La @dataclass li ha generati automaticamente.
Quando usiamo @dataclass, Python genera automaticamente i seguenti metodi:
__init__: per inizializzare gli attributi__repr__: per rappresentare l’oggetto in modo leggibile__eq__: per confrontare due oggettiEcco un altro esempio, in cui definiamo una classe per un inventario:
from typing import ClassVar
from dataclasses import dataclass
@dataclass
class InventoryItem:
"""Classe per tenere traccia di un articolo di inventario"""
name: str
unit_price: float
quantity_on_hand: int = 0
class_var: ClassVar[int] = 100
INFO
Quando vogliamo indicare che un attributo appartiene alla classe (e non all’istanza), possiamo usare ClassVar.
Queste variabili non vengono incluse nel costruttore generato automaticamente.
In alcuni casi, potremmo voler escludere certi attributi dal costruttore automatico o dall’output del __repr__.
Possiamo ottenere questo comportamento con field() dal modulo dataclasses.
from dataclasses import dataclass, field
@dataclass
class Libro:
titolo: str
autore: str
prezzo: float
_iva: float = field(default=0.1, repr=False)
INFO
field() consente di controllare come gli attributi vengono trattati dalla dataclass:
init=False: esclude l’attributo dal costruttorerepr=False: lo nasconde nella rappresentazione testualecompare=False: lo esclude dal confronto ==Quando ereditiamo da una classe che non è una @dataclass, è necessario gestire manualmente alcune parti dell’inizializzazione.
from dataclasses import dataclass
class Rectangle:
def __init__(self, height, width):
self.height = height
self.width = width
@dataclass
class Square(Rectangle):
side: float
def post_init(self):
super().init(self.side, self.side)
INFO
Il metodo __post_init__ viene eseguito automaticamente dopo il costruttore generato da @dataclass.
È utile quando servono operazioni di inizializzazione aggiuntive.
Esempio:
from dataclasses import dataclass
@dataclass
class Prodotto:
nome: str
prezzo: float
iva: float = 0.22
def __post_init__(self):
self.prezzo_lordo = self.prezzo * (1 + self.iva) Quando sia la classe base che quella derivata sono @dataclass, Python gestisce tutto automaticamente.
Vediamo un esempio:
from dataclasses import dataclass
@dataclass
class Rectangle:
width: int
length: int
@dataclass
class ColorRectangle(Rectangle):
color: str
rect = ColorRectangle(10, 10, "green")
In questo caso, non è necessario definire manualmente né il costruttore né il metodo __post_init__,
poiché tutto viene generato automaticamente da Python.
| Opzione | Significato |
|---|---|
frozen=True | Rende gli oggetti immutabili |
order=True | Genera anche i metodi di confronto <, <=, > e >= |
kw_only=True (da Python 3.10) | Rende obbligatorio passare i parametri per nome |
Esempio:
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class Punto:
x: int
y: int
Creare una @dataclass chiamata “Libro” con:
__post_init__ che calcola prezzo_ivato con IVA al 10%descrizione che restituisce una stringa tipo "Titolo di Autore - Prezzo: XX€"1) Qual è lo scopo principale del decoratore @dataclass in Python?
2) Quale modulo è necessario importare per usare le dataclass?
3) Quale dei seguenti metodi viene generato automaticamente da @dataclass?
4) Cosa fa il parametro repr=False nel campo definito con field()?
5) Come si esclude un attributo dal costruttore automatico di una dataclass?
6) Qual è lo scopo del metodo __post_init__ in una dataclass?
7) Quale opzione del decoratore dataclass rende un oggetto immutabile?
8) Quando è utile usare ClassVar all'interno di una dataclass?
9) Quale parametro del decoratore dataclass genera anche i metodi di ordinamento (<, <=, >, >=)?
10) Quale vantaggio offre l'uso di @dataclass rispetto a una classe tradizionale?