102
>>> 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__。これ<attribute '__dict__' of 'A' objects>A.__dict__.__dict__あり、いつ何かが含まれていますか?

4

3 に答える 3

112

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

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

短いバージョン:

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

長いバージョン:

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

通常のオブジェクトの場合、オブジェクトは属性を格納__dict__する別のオブジェクトを作成し、最初にそのオブジェクトにアクセスしてそこから属性を取得しようとします(記述子プロトコルを使用してクラス内の属性を検索する前、およびを呼び出す前)。クラスの記述子は、このディクショナリへのアクセスを実装します。dict__getattribute____getattr____dict__

  • a.nameそれらを順番に試すのと同じです:(がデータ記述子のtype(a).__dict__['name'].__get__(a, type(a))場合のみ)、、、。type(a).__dict__['name']a.__dict__['name']type(a).__dict__['name'].__get__(a, type(a))type(a).__dict__['name']
  • a.__dict__同じことを行いますが、明らかな理由で2番目のステップをスキップします。

__dict__インスタンスをそれ自体に格納することは不可能であるため、代わりに記述子プロトコルを介して直接アクセスされ、インスタンスの特別なフィールドに格納されます。

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

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


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

于 2011-02-02T17:26:02.523 に答える
11

次の簡単な例を試して、これをさらに理解することができます。

>>> 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
于 2016-06-06T23:52:27.873 に答える
10

探索してみましょう!

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

于 2011-02-02T17:38:05.433 に答える