Le mot de l'éditeur précédent posait la question : combien coûte ce __dict__ que chaque instance porte, sans qu'on l'ait demandé ?The previous editorial's note posed the question: how much does that __dict__ every instance carries, unasked, actually cost?
Les numéros 16 et 17 ont supposé, sans jamais le questionner, qu'une instance porte un __dict__ — un dictionnaire ordinaire, capable d'accueillir n'importe quel attribut, à n'importe quel moment de la vie de l'objet. C'est ce confort qui permet self.nouvel_attribut = valeur sans jamais déclarer nouvel_attribut à l'avance. Ce confort a un coût mesurable : un dictionnaire alloué dynamiquement, avec sa table de hachage, ses collisions possibles, sa capacité qui grandit par paliers — même pour une classe aussi simple qu'un point à deux coordonnées, fixées une fois pour toutes dans __init__ et jamais renommées. Depuis Python 3.11, CPython retarde la construction de ce dictionnaire tant que personne ne le demande : le coût a fondu, il n'a pas disparu.Issues 16 and 17 both assumed, without ever questioning it, that an instance carries a __dict__ — an ordinary dictionary, able to welcome any attribute, at any point in the object's life. That comfort is what lets self.new_attribute = value work without ever declaring new_attribute ahead of time. That comfort has a measurable cost: a dynamically allocated dictionary, with its hash table, its possible collisions, its capacity growing in steps — even for a class as simple as a two-coordinate point, fixed once and for all in __init__ and never renamed. Since Python 3.11, CPython delays building that dictionary until someone asks for it: the cost has shrunk, not vanished.
class Point: def __init__(self, x, y): self.x = x self.y = y p = Point(1, 2) print(p.__dict__) # {'x': 1, 'y': 2} -- un vrai dict, fabriqué à la demande # depuis Python 3.11, CPython range x et y directement dans l'objet et ne # construit ce dictionnaire que si on le réclame (comme ci-dessus) -- # mais chaque instance reste prête à accueillir n'importe quel nom, # alors que Point n'en utilise que 2, toujours les mêmes, fixés dès __init__. # Le coût réel se mesure au chapitre 3.
Face à une classe qui crée des milliers d'instances aux attributs fixes et connus d'avance, demande : ai-je vraiment besoin qu'on puisse ajouter n'importe quel attribut, n'importe quand — ou est-ce que je paie pour une flexibilité que je n'utilise jamais ?Facing a class that creates thousands of instances with fixed, known-in-advance attributes, ask: do I really need the ability to add any attribute, at any time — or am I paying for flexibility I never use?
Une quarantaine d'octets par instance ne pèsent rien pour un objet unique. Multipliés par un million d'instances — un parseur, un jeu de données, une simulation — la différence se compte en dizaines de mégaoctets, davantage si le __dict__ finit par être matérialisé.Forty-odd bytes per instance weigh nothing for a single object. Multiplied by a million instances — a parser, a dataset, a simulation — the difference adds up to tens of megabytes, more if the __dict__ ends up materialized.