Believemy logo purple

Tout sur dataclass en Python

Les dataclasses permettent de créer des classes immuables d'une manière condensée comparé aux classes traditionnelles.
Believemy logo

Le décorateur @dataclass est apparu avec Python 3.7 pour répondre à un besoin clair : structurer des données simplement, sans écrire de code répétitif.

C'est une solution native et élégante pour créer des classes lisibles, concises, typées et puissantes, tout en réduisant significativement le code à écrire (on y vient juste après). C’est un outil plébiscité aussi bien par les développeurs débutants que les experts.

Lorsqu’on l’applique à une classe, Python génère automatiquement plusieurs méthodes spéciales comme :

  • __init__() pour l’initialisation des attributs ;
  • __repr__() pour une représentation lisible ;
  • __eq__() pour la comparaison d’objets.

et même d’autres méthodes si on le souhaite (__lt__, __le__, etc).

Cela permet de se concentrer sur les données, tout en ayant un objet pleinement fonctionnel.

Contrairement aux namedtuple, les dataclass sont plus souples, acceptent le typage, la mutabilité ou l’immuabilité, et peuvent contenir des méthodes personnalisées.

 

Syntaxe de base et exemple

Pour utiliser une dataclass, il suffit d'ajouter @dataclass (qui est un décorateur) au-dessus d'une classe pour activer ses fonctionnalités.

Voici un petit exemple de dataclass pour illustrer toute sa puissance :

PYTHON
from dataclasses import dataclass

@dataclass
class Produit:
    nom: str
    prix: float

article = Produit("Clavier", 49.99)

print(article.nom)   # Clavier
print(article.prix)  # 49.99
print(article)       # Produit(nom='Clavier', prix=49.99)

En seulement 4 lignes, nous avons une classe typée avec un constructeur automatique. Magique, non ? 😋

Pas besoin de __init__, __str__, ni de définir les types manuellement dans des méthodes : @dataclass le fait pour nous.

 

Les paramètres clés du décorateur @dataclass

Le décorateur @dataclass peut être configuré à l’aide de paramètres optionnels, selon le comportement désiré :

ParamètreDescription
init=TrueGénère automatiquement la méthode __init__()
repr=TrueGénère __repr()__ pour faire une représentation
eq=TrueGénère __eq__() pour faire des comparaisons d'égalité
order=FalsePermet de comparer l'infériorité et la supériorité
frozen=FalseRend l'objet immuable (si True)

 

Les valeurs par défaut et les champs optionnels

Avec dataclass, on peut facilement spécifier des valeurs par défaut pour certains champs, comme dans une fonction classique :

PYTHON
from dataclasses import dataclass

@dataclass
class Article:
    nom: str
    prix: float = 0.0

Dans cet exemple, si l'on crée un Article("Câble USB"), le prix sera automatiquement 0.0.

Le module dataclasses propose également la fonction field() pour gérer des cas avancés :

PYTHON
from dataclasses import dataclass, field

@dataclass
class Commande:
    articles: list = field(default_factory=list)

Ici, default_factory est très utile pour éviter les pièges liés aux valeurs mutables partagées comme les listes ou les dictionnaires.

 

Rendre une dataclass immuable (frozen=True)

Lorsque nous ajoutons frozen=True, l’objet devient immuable : ses attributs ne peuvent plus être modifiés après la création.

PYTHON
from dataclasses import dataclass

@dataclass(frozen=True)
class Client:
    nom: str
    age: int

c = Client("Jean", 30)
# c.age = 31  ❌ Provoque une erreur : cannot assign to field

D'ailleurs, les frozen dataclass sont hashables automatiquement si leurs champs le sont. Vous pouvez ainsi les utiliser comme clés dans un dictionnaire ou dans un set.

 

Comparaison et tri avec order=True

Par défaut, une dataclass ne peut pas être triée ni comparée avec <, >, <=, >=.

Pour activer ces opérations, nous pouvons préciser le paramètre order=True.

PYTHON
from dataclasses import dataclass

@dataclass(order=True)
class Produit:
    prix: float
    nom: str

L’ordre de tri se base sur l’ordre des champs dans la déclaration : ici, les instances seront triées selon le prix, puis selon le nom si les prix sont égaux.

 

La méthode spéciale __post_init__()

La méthode __post_init__() est appelée après l’exécution de __init__() générée automatiquement par @dataclass. Elle permet de réaliser des traitements ou des vérifications personnalisées sur les attributs.

PYTHON
from dataclasses import dataclass

@dataclass
class Personne:
    nom: str
    age: int

    def __post_init__(self):
        if self.age < 0:
            raise ValueError("L'âge ne peut pas être négatif.")

Dans cet exemple même si __init__() est automatique on ajoute une logique métier spécifique sans avoir à la réécrire.

On utilise souvent __post_init__() pour valider, arrondir ou transformer des données d'entrée. 😉

 

Typage fort et type hints

L’un des grands avantages des dataclass est leur intégration native avec les annotations de type.

Chaque champ est typé avec une annotation standard, ce qui offre plusieurs bénéfices :

  • Une documentation claire ;
  • Un meilleur support avec notre IDE (auto-complétion, vérification) ;
  • L'intégration avec des outils de vérification statique (MyPy, Pyright).

Voici comme faire :

PYTHON
@dataclass
class Compte:
    identifiant: int
    solde: float
    actif: bool

Grâce à ces types les outils peuvent détecter si une mauvaise valeur est passée à l’instanciation :

PYTHON
c = Compte("abc", 50.0, True)  # 🚫 Erreur détectable avec MyPy

Le typage est recommandé, mais pas obligatoire. Toutefois, sans types, certaines fonctionnalités de dataclass ne fonctionneront pas correctement (comme la génération de __init__()).

 

dataclass vs classe classique : quelles sont les différences ?

Voyons maintenant une comparaison concrète entre une classe classique et une dataclass.

L’objectif est de mettre en évidence la réduction de code et le gain de lisibilité.

Classe classique :

PYTHON
class Produit:
    def __init__(self, nom, prix):
        self.nom = nom
        self.prix = prix

    def __repr__(self):
        return f"Produit(nom={self.nom!r}, prix={self.prix!r})"

    def __eq__(self, other):
        return isinstance(other, Produit) and self.nom == other.nom and self.prix == other.prix

 

Version dataclass :

PYTHON
from dataclasses import dataclass

@dataclass
class Produit:
    nom: str
    prix: float

Résultat : même fonctionnalité, 5 fois moins de code, plus clair, plus propre.

 

dataclass vs namedtuple

Avant dataclass, on utilisait souvent namedtuple pour créer des objets légers avec nommage de champs. Comparons-les :

Critèredataclassnamedtuple
Nécessite l'import✅ Oui (dataclasses)✅ Oui (collections)
Mutable ?✅ Oui❌ Non (immuable)
Typé ?✅ Oui❌ Non
Méthodes personnalisées ?✅ Oui❌ Pas vraiment
Héritage✅ Oui❌ Complexe
Triable ?✅ Oui✅ Oui (par défaut)

En résumé : dataclass est plus moderne et plus flexible, tandis que namedtuple reste utile si vous voulez de l’immuabilité simple sans surcharge.

 

Les options avancées avec field()

Comme nous l'avons vu, le module dataclasses fournit l’utilitaire field() pour personnaliser finement le comportement de chaque attribut.

Voyons ensemble ses paramètres :

OptionDescription
default=Valeur par défaut
default_factory=Génère dynamiquement une valeur par défaut (top pour les listes et les dictionnaires)
init=False

Ne pas inclure dans __init__()

repr=FalseExclut du __repr__()
compare=FalseExclut de __eq__() et __lt__()

Prenons cet exemple :

PYTHON
from dataclasses import dataclass, field

@dataclass
class Compteur:
    nom: str
    historique: list = field(default_factory=list, repr=False, compare=False)

Dans cet exemple :

  • historique est invisible dans __repr__() ;
  • Il ne sert pas à la comparaison entre deux objets ;
  • Il reçoit une nouvelle liste à chaque instance, sans partage de référence (évite les pièges).

Comme nous pouvons l'apercevoir, field() est un outil très puissant pour affiner les règles de nos objets dataclass, surtout dans les contextes orientés API, sérialisation ou logique métier.

 

Utiliser asdict() et astuple()

Les dataclass peuvent être converties facilement en dictionnaire ou en tuple grâce aux fonctions utilitaires asdict() et astuple() du module dataclasses.

PYTHON
from dataclasses import dataclass, asdict, astuple

@dataclass
class Produit:
    nom: str
    prix: float

p = Produit("Stylo", 2.50)

print(asdict(p))   # {'nom': 'Stylo', 'prix': 2.5}
print(astuple(p))  # ('Stylo', 2.5)

Ces conversions sont utiles :

Pour la sérialisation vers JSON ;

Pour l’affichage de debug ;

Ou pour envoyer les données via une API.

asdict() effectue une conversion récursive : si un champ contient une autre dataclass, elle sera également convertie.

 

Héritage et dataclasses imbriquées

Héritage des dataclasses

Comme les classes Python "classiques", les dataclass supportent l’héritage. Cela permet de factoriser des attributs communs ou d’enrichir des classes spécialisées.

PYTHON
from dataclasses import dataclass

@dataclass
class Personne:
    nom: str
    age: int

@dataclass
class Employe(Personne):
    poste: str

Ici, Employe hérite des champs de Personne et ajoute un champ poste.

 

Les dataclasses imbriquées

Les dataclasses imbriquées sont idéales pour représenter des objets complexes ou hiérarchisés.

PYTHON
@dataclass
class Adresse:
    ville: str
    code_postal: str

@dataclass
class Client:
    nom: str
    adresse: Adresse

c = Client("Chloé", Adresse("Paris", "75001"))
print(c.adresse.ville)  # Paris

 

Performances et limitations des dataclasses

Performances

Les dataclass sont aussi rapides que les classes classiques pour la majorité des usages, mais il convient de ne pas oublier que les méthodes automatiques ajoutent une légère surcharge à l'instanciation (c'est vraiment dérisoire).

Aussi, les objets frozen=True sont un peu plus lent car hashables évidemment.

 

Limitations

Les dataclasses ne remplacent par un ORM ou un modèle métier complet, elles sont aussi peu adaptées au objets très dynamiques ou pour l'héritage multiple.

Enfin : elles sont uniquement compatibles à partir de Python 3.7.

 

Cas pratiques d'utilisation

Les dataclass sont utilisées dans de nombreux contextes concrets. Faisons un petit tour avec quelques exemples.

Avec des données simples

PYTHON
@dataclass
class Utilisateur:
    nom: str
    email: str
    actif: bool = True

Idéal pour manipuler des objets utilisateurs dans une API.

 

Avec des configurations

PYTHON
@dataclass
class Config:
    debug: bool
    chemin: str
    version: float

 

Avec des données métiers

PYTHON
@dataclass
class Commande:
    produit: str
    quantite: int
    prix_unitaire: float

    def total(self) -> float:
        return self.quantite * self.prix_unitaire

 

Questions fréquentes sur les dataclasses

Faisons un petit point sur les questions les plus fréquentes avec les dataclasses en Python !

Est-ce que je peux ajouter des méthodes dans une dataclass ?

Oui ! Les dataclass sont des classes Python classiques, donc vous pouvez ajouter des méthodes comme dans toute autre classe.

 

Puis-je modifier les attributs d'une dataclass ?

Oui, sauf si vous avez défini frozen=True. Dans ce cas, les instances deviennent immuables.

 

Est-ce compatible avec les versions < 3.7 ?

Non. Le module dataclasses est natif à partir de Python 3.7. Pour les versions antérieures, une backport existe : pip install dataclasses.

 

Où apprendre à maîtriser Python ?

Avec notre formation !

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.