obj.attr n'est jamais un décalage mémoire — c'est une recherche, au mieux mise en cache, jamais supprimée.obj.attr is never a memory offset — it's a search, cached at best, never removed.
Le Vol. VI a détaillé comment Python résout obj.attr — d'abord le __dict__ de l'instance, puis celui de la classe, puis remontée du MRO (n°16), avec les descripteurs et property qui s'insèrent dans cette recherche (n°17), et __slots__ qui la modifie en remplaçant le dictionnaire par des emplacements fixes (n°18). Ce dernier numéro de la série pose la question restée en suspens depuis : tout cela a un coût, et ce coût n'est pas négligeable dans une boucle qui répète l'opération des millions de fois. Dans un langage à typage statique, obj.attr est résolu une fois, à la compilation, en un simple décalage mémoire. En Python, c'est en principe un appel de fonction — __getattribute__ — exécuté à chaque ligne, à chaque itération. Depuis Python 3.11, l'interpréteur spécialisant (PEP 659) raccourcit ce chemin quand il se répète à l'identique ; mais le raccourci doit être revérifié à chaque passage, et retombe sur la recherche complète dès que l'objet ou son type change de forme.Vol. VI detailed how Python resolves obj.attr — first the instance's __dict__, then the class's, then a walk up the MRO (issue №16), with descriptors and property inserting themselves into that search (issue №17), and __slots__ modifying it by replacing the dict with fixed slots (issue №18). This final issue in the series asks the question left hanging since then: all of this has a cost, and that cost isn't negligible in a loop that repeats the operation millions of times. In a statically typed language, obj.attr is resolved once, at compile time, into a simple memory offset. In Python, it's in principle a function call — __getattribute__ — executed on every line, every iteration. Since Python 3.11, the specializing interpreter (PEP 659) shortcuts that path when it repeats identically; but the shortcut must be rechecked on every pass, and falls back to the full search as soon as the object or its type changes shape.
class Point: def __init__(self, x, y): self.x = x self.y = y p = Point(1, 2) p.x # dans un langage à typage statique : un décalage mémoire fixe, # calculé une fois à la compilation -- un accès quasi gratuit # en Python : obj.__getattribute__("x") -- une RECHERCHE, faite # à l'exécution -- p.__dict__, puis type(p), puis le MRO de # type(p), exactement le mécanisme du n°16. Depuis 3.11, CPython # mémorise le résultat à chaque site d'accès -- mais doit revérifier # ce raccourci à CHAQUE passage : un cache, jamais un décalage garanti
« Que coûte, au runtime, ce que le langage m'a laissé écrire gratuitement ? » — pour obj.attr, la réponse tient dans les trois chapitres qui suivent."What does this thing the language let me write for free actually cost at runtime?" — for obj.attr, the answer is in the next three chapters.