Passer au contenu principal
eLearner.app
Module 9 · Leçon 2 sur 434/36 dans le cours~12 min
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)

Python
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

Python
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, 4

Le 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

Python
from dataclasses import dataclass, field

@dataclass
class Articolo:
    nome: str
    prezzo: float = 0.0
    tag: list[str] = field(default_factory=list)

frozen=True : immuable

Python
@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

Python
@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.

Python
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.0

Dataclasses 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

Exercice#python.m9.l2.e1
Tentatives : 0Chargement…

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)`.

Chargement de l'éditeur…
Afficher l'indice

@dataclass génère __eq__ en comparant champ par champ.

Solution disponible après 3 tentatives

Exercice de révision

Exercice#python.m9.l2.e2
Tentatives : 0Chargement…

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.

Chargement de l'éditeur…
Afficher l'indice

field(default_factory=list) garantit une nouvelle liste par instance.

Solution disponible après 3 tentatives

Défi supplémentaire

Exercice#python.m9.l2.e3
Tentatives : 0Chargement…

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`.

Chargement de l'éditeur…
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