web-dev-qa-db-fra.com

Qu'est-ce que l'attribut __dict __.__ dict__ de la classe Python?

>>> 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?

77
porgarmingduod

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:

  1. Pour une instance de classe A, l'accès à instance.__dict__ Est fourni par A.__dict__['__dict__'], Ce qui revient au même que vars(A)['__dict__'].
  2. Pour la classe A, l'accès à 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 évidentes

Comme 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.

98
Rosh Oxymoron

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'
9
vz0

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__.

8
Andrew Clark

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
7
damaZhang