Подтвердить что ты не робот

Что такое атрибут __dict __.__ dict__ класса 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?

Если я делаю A.something = 10, это переходит в A.__dict__. Что , это <attribute '__dict__' of 'A' objects> найдено в A.__dict__.__dict__, а когда оно что-то содержит?

4b9b3361

Ответ 1

Прежде всего A.__dict__.__dict__ отличается от A.__dict__['__dict__'], а первое не существует. Последний является атрибутом __dict__, который будут иметь экземпляры класса. Это объект дескриптора, который возвращает внутренний словарь атрибутов для конкретного экземпляра. Короче говоря, атрибут __dict__ объекта не может быть сохранен в объекте __dict__, поэтому он обращается через дескриптор, определенный в классе.

Чтобы понять это, вам нужно будет прочитать документацию протокола дескриптора.

Краткая версия:

  • Для экземпляра класса A доступ к instance.__dict__ предоставляется A.__dict__['__dict__'], который совпадает с vars(A)['__dict__'].
  • Для класса A доступ к A.__dict__ предоставляется type.__dict__['__dict__'] (теоретически), который совпадает с vars(type)['__dict__'].

Длинная версия:

Оба класса и объекты обеспечивают доступ к атрибутам как через оператор атрибута (реализуемый через класс или метакласс __getattribute__), так и атрибут/протокол __dict__, который используется vars(ob).

Для обычных объектов объект __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__ - это специальный прокси-объект, который претендует на роль словаря (но может и не быть внутренне) и не позволяет вам изменять его или заменять другим, Этот прокси позволяет вам, помимо всего прочего, получить доступ к атрибутам определенного для него класса и не определен в одной из его баз.

По умолчанию a vars(cls) пустого класса содержит три дескриптора - __dict__ для хранения атрибутов экземпляров, __weakref__, который используется внутри weakref, и docstring класса. Первые два могут исчезнуть, если вы определите __slots__. Тогда у вас не было бы атрибутов __dict__ и __weakref__, но вместо этого у вас был бы один атрибут класса для каждого слота. Атрибуты экземпляра затем не будут храниться в словаре, и доступ к ним будет предоставлен соответствующими дескрипторами в классе.


И, наконец, несоответствие, которое A.__dict__ отличается от A.__dict__['__dict__'], связано с тем, что атрибут __dict__, по крайней мере, никогда не искал в vars(A), поэтому для него истинно неверно для практически любой другой атрибут, который вы будете использовать. Например, A.__weakref__ - это то же самое, что и A.__dict__['__weakref__']. Если эта несогласованность не существует, использование A.__dict__ не будет работать, и вам придется всегда использовать vars(A).

Ответ 2

Так как 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'

Ответ 3

Давайте немного изучим!

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

Ответ 4

Вы можете попробовать следующий простой пример, чтобы узнать больше об этом:

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