web-dev-qa-db-ja.com

Pythonクラスの__dict __.__ dict__属性は何ですか?

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

私が行った場合 A.something = 10、これはA.__dict__is this <attribute '__dict__' of 'A' objects>で見つかりましたA.__dict__.__dict__、およびいつ何かが含まれますか?

77
porgarmingduod

まず、A.__dict__.__dict__A.__dict__['__dict__']とは異なり、前者は存在しません。後者は、クラスのインスタンスが持つ__dict__属性です。特定のインスタンスの属性の内部辞書を返す記述子オブジェクトです。つまり、オブジェクトの__dict__属性はオブジェクトの__dict__に格納できないため、クラスで定義された記述子を介してアクセスされます。

これを理解するには、 記述子プロトコルのドキュメント を読む必要があります。

ショートバージョン:

  1. クラスAのインスタンスの場合、instance.__dict__へのアクセスはA.__dict__['__dict__']によって提供されます。これはvars(A)['__dict__']と同じです。
  2. クラスAの場合、A.__dict__へのアクセスは、type.__dict__['__dict__'](理論上)によって提供されます。これはvars(type)['__dict__']と同じです。

ロングバージョン:

クラスとオブジェクトは両方とも、属性演算子(クラスまたはメタクラスの__getattribute__を介して実装)と、vars(ob)で使用される__dict__属性/プロトコルの両方を介して属性へのアクセスを提供します。

通常のオブジェクトの場合、__dict__オブジェクトは属性を格納する別のdictオブジェクトを作成し、__getattribute__は最初にそれにアクセスして属性を取得しようとします(検索を試みる前に)記述子プロトコルを使用して、__getattr__)を呼び出す前に、クラスの属性。クラスの__dict__記述子は、この辞書へのアクセスを実装します。

  • x.nameは、x.__dict__['name']type(x).name.__get__(x, type(x))type(x).nameを順番に試すのと同じです。
  • x.__dict__は同じことを行いますが、明白な理由で最初のコードをスキップします

__dict__ of instanceをインスタンスの__dict__に保存することは不可能なので、代わりに記述子プロトコルを介して直接アクセスし、インスタンスの特別なフィールドに保存します。

同様のシナリオはクラスにも当てはまりますが、__dict__は辞書のふりをする特別なプロキシオブジェクトですが(内部的にはそうでない場合があります)、変更したり別のものに置き換えたりすることはできません。このプロキシを使用すると、他のすべてのクラスの中で、そのベースの1つで定義されていないクラス固有のクラスの属性にアクセスできます。

デフォルトでは、空のクラスのvars(cls)は、インスタンスの属性を格納する__dict__weakrefによって内部的に使用される__weakref__、およびクラス。 __slots__を定義すると、最初の2つはなくなる可能性があります。その場合、__dict__および__weakref__属性はありませんが、代わりに各スロットに1つのクラス属性があります。インスタンスの属性はディクショナリに保存されず、クラス内のそれぞれの記述子によってそれらへのアクセスが提供されます。


そして最後に、A.__dict__A.__dict__['__dict__']と異なるという矛盾は、属性__dict__が例外としてnevervars(A)で検索したため、実際に使用する他の属性には当てはまりません。たとえば、A.__weakref__A.__dict__['__weakref__']と同じものです。この矛盾が存在しない場合、A.__dict__を使用しても機能せず、代わりにvars(A)を常に使用する必要があります。

98
Rosh Oxymoron

A.__dict__A属性を格納する辞書であるため、A.__dict__['__dict__']は同じA.__dict__属性への直接参照です。

A.__dict__には、それ自体への(種類の)参照が含まれています。 「種類」の部分は、式A.__dict__が通常のdictproxyの代わりにdictを返す理由です。

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

探索してみましょう!

>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects>

何だろう?

>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>

getset_descriptorオブジェクトにはどのような属性がありますか?

>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>

そのdictproxyのコピーを作成することにより、いくつかの興味深い属性、特に__objclass__および__name__を見つけることができます。

>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__
(<class '__main__.A'>, '__dict__')

__objclass__Aへの参照であり、__name__は単なる文字列'__dict__'であり、属性の名前でしょうか?

>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__
True

そこにある! A.__dict__['__dict__']は、A.__dict__を参照し直すことができるオブジェクトです。

8
Andrew Clark

次の簡単な例を試して、これをさらに理解してください。

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

上記の例から、クラスオブジェクトの属性はクラスによって保存され、クラスの属性はクラスによって保存されているようです。これはメタクラスです。これは以下によっても検証されます。

>>> a.__dict__ == A.__getattribute__(a, '__dict__')
True
>>> A.__dict__ == type.__getattribute__(A, '__dict__')
True
7
damaZhang