Aperçu
La question a été abordée. Cependant, cette réponse ajoute quelques exemples pratiques pour aider à la compréhension de base des classes de données.
Que sont exactement les classes de données Python et quand est-il préférable de les utiliser?
- générateurs de code : générer un code standard; vous pouvez choisir d'implémenter des méthodes spéciales dans une classe régulière ou de les faire implémenter automatiquement par une classe de données.
- conteneurs de données : structures qui contiennent des données (par exemple, des tuples et des dicts), souvent avec des points d'accès aux attributs tels que les classes
namedtuple
et autres .
"tuples nommés mutables avec [s] par défaut"
Voici ce que signifie cette dernière phrase:
- mutable : par défaut, les attributs de classe de données peuvent être réaffectés. Vous pouvez éventuellement les rendre immuables (voir les exemples ci-dessous).
- namedtuple : vous avez un accès aux attributs pointillé comme une
namedtuple
classe ou une classe normale.
- par défaut : vous pouvez attribuer des valeurs par défaut aux attributs.
Par rapport aux classes courantes, vous économisez principalement sur la saisie de code standard.
Caractéristiques
Ceci est un aperçu des fonctionnalités de la classe de données (TL; DR? Voir le tableau récapitulatif dans la section suivante).
Ce que vous obtenez
Voici les fonctionnalités que vous obtenez par défaut à partir des classes de données.
Attributs + représentation + comparaison
import dataclasses
@dataclasses.dataclass
#@dataclasses.dataclass() # alternative
class Color:
r : int = 0
g : int = 0
b : int = 0
Ces valeurs par défaut sont fournies en définissant automatiquement les mots-clés suivants sur True
:
@dataclasses.dataclass(init=True, repr=True, eq=True)
Ce que vous pouvez activer
Des fonctionnalités supplémentaires sont disponibles si les mots-clés appropriés sont définis sur True
.
Ordre
@dataclasses.dataclass(order=True)
class Color:
r : int = 0
g : int = 0
b : int = 0
Les méthodes de classement sont maintenant implémentées (opérateurs de surcharge:) < > <= >=
, de la même manière functools.total_ordering
qu'avec des tests d'égalité plus forts.
Hashable, Mutable
@dataclasses.dataclass(unsafe_hash=True) # override base `__hash__`
class Color:
...
Bien que l'objet soit potentiellement mutable (éventuellement indésirable), un hachage est implémenté.
Hashable, immuable
@dataclasses.dataclass(frozen=True) # `eq=True` (default) to be immutable
class Color:
...
Un hachage est maintenant implémenté et la modification de l'objet ou l'attribution d'attributs est interdite.
Dans l'ensemble, l'objet peut être haché si l'un unsafe_hash=True
ou l' autre frozen=True
.
Voir également la table logique de hachage d' origine avec plus de détails.
Ce que tu n'obtiens pas
Pour obtenir les fonctionnalités suivantes, des méthodes spéciales doivent être implémentées manuellement:
Déballage
@dataclasses.dataclass
class Color:
r : int = 0
g : int = 0
b : int = 0
def __iter__(self):
yield from dataclasses.astuple(self)
Optimisation
@dataclasses.dataclass
class SlottedColor:
__slots__ = ["r", "b", "g"]
r : int
g : int
b : int
La taille de l'objet est maintenant réduite:
>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888
Dans certaines circonstances, __slots__
améliore également la vitesse de création des instances et d'accès aux attributs. De plus, les emplacements n'autorisent pas les attributions par défaut; sinon, a ValueError
est levé.
En savoir plus sur les machines à sous dans cet article de blog .
Sommaire
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Feature | Keyword | Example | Implement in a Class |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes | init | Color().r -> 0 | __init__ |
| Representation | repr | Color() -> Color(r=0, g=0, b=0) | __repr__ |
| Comparision* | eq | Color() == Color(0, 0, 0) -> True | __eq__ |
| | | | |
| Order | order | sorted([Color(0, 50, 0), Color()]) -> ... | __lt__, __le__, __gt__, __ge__ |
| Hashable | unsafe_hash/frozen | {Color(), {Color()}} -> {Color(r=0, g=0, b=0)} | __hash__ |
| Immutable | frozen + eq | Color().r = 10 -> TypeError | __setattr__, __delattr__ |
| | | | |
| Unpacking+ | - | r, g, b = Color() | __iter__ |
| Optimization+ | - | sys.getsizeof(SlottedColor) -> 888 | __slots__ |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
+ Ces méthodes ne sont pas générées automatiquement et nécessitent une implémentation manuelle dans une classe de données.
* __ne__
n'est pas nécessaire et n'est donc pas implémenté .
Caractéristiques supplémentaires
Post-initialisation
@dataclasses.dataclass
class RGBA:
r : int = 0
g : int = 0
b : int = 0
a : float = 1.0
def __post_init__(self):
self.a : int = int(self.a * 255)
RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)
Héritage
@dataclasses.dataclass
class RGBA(Color):
a : int = 0
Les conversions
Convertissez une classe de données en tuple ou en dict, de manière récursive :
>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}
Limites
Références
- De R. Hettinger le discours sur Dataclasses: Le générateur de code pour mettre fin à tous les générateurs de code
- T. Hunner Le discours sur Easier classes: classes Python sans tous les Cruft
- Python Documentation sur les détails de hachage
- Guide du vrai Python sur le guide ultime pour les classes de données en Python 3.7
- Article de blog de A. Shaw sur Une brève visite des classes de données Python 3.7
- Dépôt github d' E. Smith sur les classes de données
namedtuple
s sont immuables et ne peuvent pas avoir de valeurs par défaut pour les attributs, alors que les classes de données sont modifiables et peuvent les avoir.