__slots__ est un attribut spécial de la classe qui limite l'ensemble des attributs d'instance autorisés, économise de la mémoire et accélère l'accès aux attributs. L'utilisation de __slots__ est particulièrement pertinente pour un grand nombre d'objets similaires.
Contexte :
L'apparition de __slots__ est liée au fait que, par défaut, chaque instance d'un objet Python dispose d'un dictionnaire d'attributs (__dict__), ce qui est pratique mais coûteux en mémoire. Pour un million d'objets avec un petit ensemble de champs, un overhead significatif se produit.
Problème :
Les instances d'une classe ordinaire peuvent être étendues dynamiquement, ce qui est pratique mais inefficace. L'application de __slots__ limite l'ajout dynamique de nouveaux attributs, supprime le dictionnaire d'attributs de l'instance, économise de la mémoire et accélère l'accès.
Solution :
Décrivez la liste des champs autorisés dans l'attribut __slots__:
class Point: __slots__ = ('x', 'y') def __init__(self, x, y): self.x = x self.y = y p = Point(1, 2) p.z = 3 # AttributeError: l'objet 'Point' n'a pas d'attribut 'z'
Caractéristiques clés :
Peut-on créer une instance d'une classe avec slots et ensuite ajouter un attribut non présent dans la liste ?
Non. Lorsqu'on essaie d'ajouter un attribut absent de la liste slots, un AttributeError sera levé. Cela impose des limites à l'extensibilité de l'objet.
Peut-on hériter d'une classe avec slots et ajouter de nouveaux champs ?
Oui, mais chaque héritier doit déclarer ses propres slots. Dans ce cas, les slots parents et actuels sont combinés. Cependant, si on ne déclare pas slots dans l'héritier, le descendant retrouvera à nouveau dict !
Est-ce que slots fonctionne pour les types immuables ?
Oui, mais il faut mettre en œuvre des mesures supplémentaires pour rendre l'objet à slots immuable (par exemple, via property sans setter).
Classe ordinaire pour un point :
class Point: def __init__(self, x, y): self.x = x self.y = y points = [Point(i, i) for i in range(1_000_000)]
Avantages :
Inconvénients :
Classe similaire, mais avec des slots :
class Point: __slots__ = ('x', 'y') def __init__(self, x, y): self.x = x self.y = y points = [Point(i, i) for i in range(1_000_000)]
Avantages :
Inconvénients :