Tout d'abord A.__dict__.__dict__
est différent de A.__dict__['__dict__']
. Le premier n'existe pas et le second est le __dict__
que les instances de la classe auront. C'est un objet descripteur de données qui renvoie le dictionnaire interne des attributs pour l'instance spécifique. En bref, l'objet __dict__
d'un objet ne peut pas être stocké dans l'objet __dict__
On y accède donc par un descripteur défini dans la classe.
Pour comprendre cela, il faut lire le document suivant documentation du protocole des descripteurs .
La version courte :
- Pour une instance
a
d'une classe A
l'accès à a.__dict__
est fourni par A.__dict__['__dict__']
qui est identique à vars(A)['__dict__']
.
- Pour une classe
A
l'accès à A.__dict__
est fourni par type.__dict__['__dict__']
(en théorie), ce qui est identique à vars(type)['__dict__']
.
La version longue :
Les classes et les objets permettent d'accéder aux attributs par l'intermédiaire de l'opérateur d'attributs (mis en œuvre via l'opérateur d'attributs de la classe ou de la métaclasse). __getattribute__
), et le __dict__
attribut/protocole qui est utilisé par vars(ob)
.
Pour les objets normaux, le __dict__
crée un objet séparé dict
qui stocke les attributs, et __getattribute__
essaie d'abord d'y accéder et de récupérer les attributs à partir de là (avant d'essayer de rechercher l'attribut dans la classe en utilisant le protocole de descripteur, et avant d'appeler le fichier __getattr__
). Le site __dict__
Le descripteur de la classe implémente l'accès à ce dictionnaire.
-
a.name
est équivalent à essayer ceux dans l'ordre : type(a).__dict__['name'].__get__(a, type(a))
(uniquement si type(a).__dict__['name']
es un données descripteur), a.__dict__['name']
, type(a).__dict__['name'].__get__(a, type(a))
, type(a).__dict__['name']
.
-
a.__dict__
fait la même chose mais saute la deuxième étape pour des raisons évidentes.
Comme il est impossible pour le __dict__
d'une instance pour être stockée en elle-même, on y accède directement par le protocole des descripteurs et elle est stockée dans un champ spécial de l'instance.
Un scénario similaire est vrai pour les classes, bien que leur __dict__
est un objet proxy spécial qui prétend être un dictionnaire (mais qui peut ne pas l'être en interne), et qui ne vous permet pas de le modifier ou de le remplacer par un autre. Ce proxy vous permet, entre autres, d'accéder aux attributs d'une classe qui lui sont spécifiques, et qui ne sont pas définis dans l'une de ses bases.
Par défaut, un vars(cls)
d'une classe vide porte trois descripteurs : __dict__
pour stocker les attributs des instances, __weakref__
qui est utilisé en interne par weakref
y __doc__
la docstring de la classe. Les deux premiers peuvent disparaître si vous définissez __slots__
. Alors vous n'auriez pas __dict__
y __weakref__
mais à la place, il y aurait un seul attribut de classe pour chaque slot. Les attributs de l'instance ne seraient alors pas stockés dans un dictionnaire, et l'accès à ceux-ci serait fourni par les descripteurs respectifs dans la classe.
Et enfin, l'incohérence que A.__dict__
est différent de A.__dict__['__dict__']
c'est parce que l'attribut __dict__
est, par exception, jamais regardé en haut vars(A)
Ce qui est vrai pour lui ne l'est pas pour pratiquement tous les autres attributs que vous pourriez utiliser. Par exemple, A.__weakref__
est la même chose que A.__dict__['__weakref__']
. Si cette incohérence n'existait pas, en utilisant A.__dict__
ne fonctionnerait pas, et vous devriez toujours utiliser vars(A)
à la place.
13 votes
Un exemple de variable plus approprié aurait été
ive
. Au moins, ça aurait fait de ce film un plusA.__dict__['ive']
question ;) Je vais m'en sortir