Leçons du module (2/4)
dataclasses : classes de données sans boilerplate
Le décorateur @dataclass (module dataclasses) génère automatiquement __init__, __repr__ et __eq__ pour les classes dont le seul rôle est de contenir des données. Moins de code répétitif, un code plus clair.
Sans dataclass (code répétitif)
class Punto:
def __init__(self, x: float, y: float) -> None:
self.x = x
self.y = y
def __repr__(self) -> str:
return f"Punto(x={self.x}, y={self.y})"
def __eq__(self, other) -> bool:
if not isinstance(other, Punto):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)Avec dataclass
from dataclasses import dataclass
@dataclass
class Punto:
x: float
y: float
p = Punto(3, 4)
p # Punto(x=3, y=4) ← __repr__ gratis
Punto(3, 4) == Punto(3, 4) # True ← __eq__ gratis
p.x, p.y # 3, 4Le modèle : déclarer les champs sous forme d'annotations de classe (nom: type), éventuellement avec une valeur par défaut. @dataclass génère le reste.
Valeurs par défaut et default_factory
from dataclasses import dataclass, field
@dataclass
class Articolo:
nome: str
prezzo: float = 0.0
tag: list[str] = field(default_factory=list)frozen=True : immuable
@dataclass(frozen=True)
class Coordinata:
lat: float
lon: float
c = Coordinata(45.4, 9.2)
c.lat = 99 # FrozenInstanceError!Les dataclasses frozen sont également hashables : vous pouvez les utiliser comme clés de dictionnaire ou éléments d'ensemble.
order=True : comparaisons automatiques
@dataclass(order=True)
class Voto:
valore: int
Voto(10) < Voto(20) # True
sorted([Voto(30), Voto(10), Voto(20)])Génère __lt__, __le__, __gt__, __ge__ en comparant les champs dans l'ordre où ils ont été déclarés.
Méthodes normales
Les dataclasses restent des classes normales : vous pouvez y ajouter des méthodes.
import math
from dataclasses import dataclass
@dataclass
class Punto:
x: float
y: float
def distanza_dall_origine(self) -> float:
return math.hypot(self.x, self.y)
Punto(3, 4).distanza_dall_origine() # 5.0Dataclasses immuables et efficaces
Vous pouvez rendre une dataclass immuable en passant l'argument frozen=True au décorateur : @dataclass(frozen=True). Cela lève une erreur lors de toute tentative de modification des attributs après l'instanciation, rendant les instances sûres pour les environnements concurrents ou pour une utilisation en tant que clés de dictionnaire.
À vous de jouer
Créez une dataclass `Book` avec les champs `title: str` et `pages: int`. Instanciez-la sous la forme `l = Book('Moby Dick', 635)`. Évaluez `l == Book('Moby Dick', 635)`.
Afficher l'indice
@dataclass génère __eq__ en comparant champ par champ.
Solution disponible après 3 tentatives
Exercice de révision
Créez une dataclass `Basket` avec le champ `items: list[str]` ayant default_factory=list. Instanciez `c1 = Basket()` et `c2 = Basket()`, ajoutez 'mela' à c1.items. Évaluez `(c1.items, c2.items)` pour vérifier qu'il ne s'agit PAS de la même liste.
Afficher l'indice
field(default_factory=list) garantit une nouvelle liste par instance.
Solution disponible après 3 tentatives
Défi supplémentaire
Importez `dataclass` depuis `dataclasses`. Créez une dataclass `Point` contenant deux coordonnées float `x` et `y`. Instanciez un point avec `x=1.5` et `y=2.5` en le stockant dans `p`. Enfin, évaluez `p`.
Afficher l'indice
Utilisez @dataclass au-dessus de la classe Point, et déclarez x: float et y: float.
Solution disponible après 3 tentatives