Nouveauté de la version 3.7
Python 3.7 apporte un nouveauté pour simplifier la définition de classes dites “de données” ; ce type de classes s’applique pour des objets qui sont essentiellement un assemblage de quelques champs de données.
Aperçu¶
La raison d’être de dataclass est de fournir - encore un - moyen de définir des classes d’enregistrements.
Voici par exemple comment on pourrait définir une classe Personne:
from dataclasses import dataclass@dataclass
class Personne:
nom: str
age: int
email: str = ""
Personne(nom='jean', age=12)Personne(nom='jean', age=12, email='')Comme vous le voyez, on n’a pas eu besoin d’implémenter une dunder __repr__ pour obtenir une présentation déjà plus agréable que <__main__.Personne at 0x10ac991e0>
Surcharge¶
On n’est pas obligé d’adopter toutes les dunder automatiques; par exemple je peux toujours définir mon propre repr() comme d’habitude :
@dataclass
class PrettyPersonne:
nom: str
age: int
email: str = ""
def __repr__(self):
return f"{self.nom} <{self.email}>, {self.age} ans"
PrettyPersonne(nom='alice', age=25, email='alice@example.com')alice <alice@example.com>, 25 ansUne fois qu’on a dit ça, il me semble personnellement plus propre d’être explicite, et d’indiquer au décorateur qu’on va se charger du repr; mais bon...
# on peut aussi être plus explicite
@dataclass(repr=False)
class PrettyPersonne:
nom: str
age: int
email: str = ""
def __repr__(self):
return f"{self.nom} <{self.email}>, {self.age} ans"
PrettyPersonne(nom='alice', age=25, email='alice@example.com')The history saving thread hit an unexpected error (OperationalError('attempt to write a readonly database')).History will not be written to the database.alice <alice@example.com>, 25 ans
Instances non mutables¶
En fait ça va beaucoup plus loin que cela, la dataclasse se retrouve avec pas mal de dunder méthodes implémentées gratuitement pour nous.
Nous reprenons ici le même scénario d’ensemble de points que nous avons déjà rencontré plusieurs fois; remarquez que la classe Point sait correctement comparer et hasher ses objets, et on va pouvoir les ranger dans un ensemble pour éliminer les doublons, sans avoir besoin de redéfinir les dunder __eq__ et __hash__ qu’il aurait fallu faire si on n’avait pas utilisé dataclass :
Enfin on illustre ici le fait que décorateur dataclass accepte divers arguments pour choisir le comportement de certains aspects de la classe. Reportez-vous à la documentation pour une liste complète, mais voici un exemple qui utilise frozen=True qui nous permet de créer des instances non mutables.
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: float
y: floatp1, p2, p3 = Point(1, 1), Point(1, 1), Point(1, 1)s = {p1, p2}
len(s)1p3 in sTruetry:
p1.x = 10
except Exception as e:
print(f"OOPS {type(e)}")OOPS <class 'dataclasses.FrozenInstanceError'>
Résumé¶
Donc bref, j’espère vous avoir convaincu que ce trait de dataclass permet d’éviter pas mal de code de type boilerplate, et comme chacun sait: less code, fewer bugs, donc n’hésitez pas à user et abuser de ce trait, d’autant qu’à présent la version 3.7 est largement acquise !
Pour aller plus loin¶
Vous pouvez vous rapporter
au PEP 557 qui a abouti au consensus, et