@dataclass

Un décorateur qui génère __init__, __repr__ et __eq__ à partir des champs annotés d’une classe qui sert à stocker des données.

Renvoie
La même classe, complétée par les méthodes générées.
Sur cette page

Exemple

#
Python
from dataclasses import dataclass, field

@dataclass
class Student:
    name: str
    world: int
    points: int = 0
    badges: list[str] = field(default_factory=list)

lea = Student("Léa", 7)
lea.points += 40
lea.badges.append("Start")

print(lea)
print(lea == Student("Léa", 7, 40, ["Start"]))
print(Student("Lucas", 2))
Résultatrésultat enregistré, vous pouvez le vérifier
Student(name='Léa', world=7, points=40, badges=['Start'])
True
Student(name='Lucas', world=2, points=0, badges=[])

Définition et utilisation

#

Le décorateur @dataclass du module dataclasses supprime le code répétitif des classes qui stockent des données. Il suffit de lister les champs avec leurs annotations de type (name: str) : le décorateur génère un __init__ dont les paramètres suivent le même ordre, un __repr__ lisible et un __eq__ qui compare les objets champ par champ.

Les champs peuvent avoir des valeurs par défaut (points: int = 0), mais ceux qui n’en ont pas doivent venir avant. Pour utiliser une liste, un dictionnaire ou un autre objet modifiable comme valeur par défaut, passez par field(default_factory=list) : chaque objet reçoit alors sa propre copie. Les étapes supplémentaires après la création de l’objet, comme une validation ou le calcul d’un champ auxiliaire, vont dans la méthode __post_init__.

Les paramètres du décorateur modifient le comportement de la classe : frozen=True interdit de modifier les champs, et order=True ajoute les comparaisons < et >. La fonction asdict() transforme l’objet en dictionnaire, par exemple pour l’enregistrer en JSON, et replace() crée une copie avec certains champs modifiés. Les annotations de type ne sont pas vérifiées à l’exécution : elles servent aux éditeurs et à des outils comme mypy.

Syntaxe

#
Syntaxe
from dataclasses import dataclass, field

@dataclass
class Nom:
    champ: type
    champ_avec_defaut: type = valeur
    liste: list[type] = field(default_factory=list)

@dataclass(frozen=True, order=True)
class Autre:
    ...

Paramètres

#
  • frozen

    bool, False par défaut

    Si True, modifier un champ lève FrozenInstanceError, et les objets peuvent servir de clés de dictionnaire et d’éléments d’ensemble.
  • order

    bool, False par défaut

    Ajoute les opérateurs <, <=, > et >=, qui comparent les champs un par un, comme des tuples.
  • slots

    bool, False par défaut

    Crée la classe avec __slots__ : les objets occupent moins de mémoire, et l’affectation à un champ inexistant lève une erreur.
  • kw_only

    bool, False par défaut

    Impose de passer tous les champs par leur nom lors de la création de l’objet.

Plus d’exemples

#
Objets immuables avec tri
Python
from dataclasses import dataclass, field

@dataclass(frozen=True, order=True)
class Result:
    points: int
    name: str = field(compare=False)

results = [Result(78, "Lucas"), Result(92, "Léa"), Result(85, "Emma")]
print([r.name for r in sorted(results, reverse=True)])

best = max(results)
try:
    best.points = 100
except AttributeError as error:
    print(type(error).__name__, error)
Résultatrésultat enregistré, vous pouvez le vérifier
['Léa', 'Emma', 'Lucas']
FrozenInstanceError cannot assign to field 'points'

field(compare=False) exclut le champ des comparaisons : les résultats sont donc triés uniquement selon les points.

Validation et champ calculé dans __post_init__
Python
from dataclasses import dataclass, field

@dataclass
class Exercise:
    number: str
    exp: int
    world: int = field(init=False)

    def __post_init__(self):
        if self.exp < 0:
            raise ValueError("L'EXP ne peut pas être négatif")
        self.world = int(self.number.split("_")[0])

task = Exercise("7_3", 15)
print(task.world)
print(task)
Résultatrésultat enregistré, vous pouvez le vérifier
7
Exercise(number='7_3', exp=15, world=7)
asdict() et replace()
Python
import json
from dataclasses import asdict, dataclass, replace

@dataclass
class Course:
    title: str
    modules: int
    premium: bool = False

python = Course("Python", 12)
advanced = replace(python, title="Python avancé", premium=True)

print(advanced)
print(asdict(python))
print(json.dumps(asdict(advanced), ensure_ascii=False))
Résultatrésultat enregistré, vous pouvez le vérifier
Course(title='Python avancé', modules=12, premium=True)
{'title': 'Python', 'modules': 12, 'premium': False}
{"title": "Python avancé", "modules": 12, "premium": true}

Bonnes pratiques

#
  • Créez les champs à valeur par défaut modifiable avec field(default_factory=list). L’écriture badges: list = [] lève une ValueError dès la définition de la classe.
  • Les types des annotations ne sont pas vérifiés : Student(name=123, world="sept") crée donc un objet sans erreur. Pour valider, utilisez __post_init__ ou la bibliothèque pydantic.
  • Les objets avec frozen=True se partagent sans risque entre les parties d’un programme, car personne ne peut les modifier par accident.

Entrées associées

#

Vous voyez une erreur ou il manque un exemple ? Écrivez-nous.