>>> class A(object): pass
...
>>> A.__dict__
<dictproxy object at 0x173ef30>
>>> A.__dict__.__dict__
Traceback (most recent call last):
File "<string>", line 1, in <fragment>
AttributeError: 'dictproxy' object has no attribute '__dict__'
>>> A.__dict__.copy()
{'__dict__': <attribute '__dict__' of 'A' objects> ... }
>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects> # What is this object?
Si je fais A.something = 10
, cela va dans A.__dict__
. Qu'est-ce que est this <attribute '__dict__' of 'A' objects>
trouvé dans A.__dict__.__dict__
et quand contient-il quelque chose?
Tout d'abord, A.__dict__.__dict__
Est différent de A.__dict__['__dict__']
, Et l'ancien n'existe pas. Le dernier est l'attribut __dict__
Que les instances de la classe auraient. C'est un objet descripteur qui renvoie le dictionnaire interne d'attributs pour l'instance spécifique. En bref, l'attribut __dict__
D'un objet ne peut pas être stocké dans l'objet __dict__
, Il est donc accessible via un descripteur défini dans la classe.
Pour comprendre cela, vous devez lire le documentation du protocole de descripteur .
La version courte:
A
, l'accès à instance.__dict__
Est fourni par A.__dict__['__dict__']
, Ce qui revient au même que vars(A)['__dict__']
.A.__dict__
Est fourni par type.__dict__['__dict__']
(En théorie), ce qui revient au même que vars(type)['__dict__']
.La version longue:
Les classes et les objets fournissent un accès aux attributs via l'opérateur d'opérateur (implémenté via la classe __getattribute__
De la métaclasse ou de la métaclasse) et l'attribut/protocole __dict__
Utilisé par vars(ob)
. .
Pour les objets normaux, l’objet __dict__
Crée un objet dict
distinct, qui stocke les attributs, et __getattribute__
Tente d’abord d’y accéder et d’obtenir les attributs à partir de là (avant de tenter de recherchez l'attribut dans la classe en utilisant le protocole de descripteur et avant d'appeler __getattr__
). Le descripteur __dict__
De la classe implémente l'accès à ce dictionnaire.
x.name
Équivaut à essayer les éléments suivants: x.__dict__['name']
, type(x).name.__get__(x, type(x))
, type(x).name
x.__dict__
Fait la même chose mais saute le premier pour des raisons évidentesComme il est impossible que le __dict__
De instance
soit stocké dans __dict__
De l'instance, il est directement accessible via le protocole de descripteur et stocké dans un champ spécial dans l'instance.
Un scénario similaire est vrai pour les classes, bien que leur __dict__
Soit un objet proxy spécial prétendant être un dictionnaire (mais qui pourrait ne pas être interne), et ne vous permet pas de le changer 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 définis dans aucune de ses bases.
Par défaut, une vars(cls)
d'une classe vide contient trois descripteurs - __dict__
Pour stocker les attributs des instances, __weakref__
Utilisé en interne par weakref
. et la docstring de la classe. Les deux premiers risquent de disparaître si vous définissez __slots__
. Dans ce cas, vous n’auriez pas les attributs __dict__
Et __weakref__
, Mais vous auriez un attribut de classe unique pour chaque emplacement. Les attributs de l'instance ne seraient alors pas stockés dans un dictionnaire et leur accès serait fourni par les descripteurs respectifs de la classe.
Enfin, l’incohérence selon laquelle A.__dict__
Est différent de A.__dict__['__dict__']
Est due au fait que l’attribut __dict__
Est, par exception, jamais regarda dans vars(A)
, donc ce qui est vrai pour ce n'est pas vrai pour pratiquement tout autre attribut que vous utiliseriez. Par exemple, A.__weakref__
Est identique à A.__dict__['__weakref__']
. Si cette incohérence n'existait pas, utiliser A.__dict__
Ne fonctionnerait pas et vous devriez toujours utiliser vars(A)
à la place.
Puisque A.__dict__
est un dictionnaire contenant A
attributs, A.__dict__['__dict__']
est la référence directe à ce même A.__dict__
attribut.
A.__dict__
contient une (sorte de) référence à lui-même. La partie "genre" explique pourquoi l'expression A.__dict__
renvoie un dictproxy
au lieu d'un dict
normal.
>>> class B(object):
... "Documentation of B class"
... pass
...
>>> B.__doc__
'Documentation of B class'
>>> B.__dict__
<dictproxy object at 0x00B83590>
>>> B.__dict__['__doc__']
'Documentation of B class'
Permet de faire des explorations!
>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects>
Je me demande ce que c'est?
>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>
Quels attributs fait un getset_descriptor
objet ont?
>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>
En faisant une copie de ce dictproxy
nous pouvons trouver des attributs intéressants, en particulier __objclass__
et __name__
.
>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__
(<class '__main__.A'>, '__dict__')
Alors __objclass__
est une référence à A
et __name__
n'est que la chaîne '__dict__'
, nom d'un attribut peut-être?
>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__
True
Là nous l'avons! A.__dict__['__dict__']
est un objet qui peut renvoyer à A.__dict__
.
Vous pouvez essayer l’exemple simple suivant pour mieux comprendre ceci:
>>> class A(object): pass
...
>>> a = A()
>>> type(A)
<type 'type'>
>>> type(a)
<class '__main__.A'>
>>> type(a.__dict__)
<type 'dict'>
>>> type(A.__dict__)
<type 'dictproxy'>
>>> type(type.__dict__)
<type 'dictproxy'>
>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>
>>> type(type.__dict__['__dict__'])
<type 'getset_descriptor'>
>>> a.__dict__ == A.__dict__['__dict__'].__get__(a)
True
>>> A.__dict__ == type.__dict__['__dict__'].__get__(A)
True
>>> a.__dict__ == type.__dict__['__dict__'].__get__(A)['__dict__'].__get__(a)
True
Dans l'exemple ci-dessus, il semble que les attributs des objets de classe soient stockés par leur classe, tandis que les attributs de la classe sont stockés par leur classe, qui sont des métaclasses. Ceci est également validé par:
>>> a.__dict__ == A.__getattribute__(a, '__dict__')
True
>>> A.__dict__ == type.__getattribute__(A, '__dict__')
True