>>> 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__
, и когда оно содержит что - то?
python
class
metaprogramming
magic-methods
поргарминг
источник
источник
ive
. По крайней мере, это вызвало бы еще большеA.__dict__['ive']
вопросов;) Я увижу себяОтветы:
Прежде всего
A.__dict__.__dict__
отличается отA.__dict__['__dict__']
, а первого не существует. Последний - это__dict__
атрибут, который будут иметь экземпляры класса. Это объект-дескриптор, который возвращает внутренний словарь атрибутов для конкретного экземпляра. Короче говоря,__dict__
атрибут объекта не может быть сохранен в объекте__dict__
, поэтому доступ к нему осуществляется через дескриптор, определенный в классе.Чтобы понять это, вам нужно прочитать документацию по протоколу дескриптора .
Краткая версия:
A
, доступ кinstance.__dict__
обеспечиваетсяA.__dict__['__dict__']
который является таким же , какvars(A)['__dict__']
.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__
изinstance
хранить в__dict__
экземпляре, он доступен через протокол дескриптора непосредственно вместо этого, и хранится в специальном поле в экземпляре.Аналогичный сценарий верен для классов, хотя они
__dict__
представляют собой специальный прокси-объект, который притворяется словарем (но может не быть внутренним) и не позволяет вам изменять его или заменять другим. Этот прокси позволяет вам, помимо всего прочего, получить доступ к атрибутам класса, которые являются специфическими для него и не определены ни в одной из его баз.По умолчанию a
vars(cls)
пустого класса содержит три дескриптора -__dict__
для хранения атрибутов экземпляров,__weakref__
которые используются внутриweakref
, и строки документации класса. Первые два могут исчезнуть, если вы определитесь__slots__
. Тогда у вас не будет__dict__
и__weakref__
атрибуты, но вместо этого вы бы один атрибут класса для каждого слота. Тогда атрибуты экземпляра не будут храниться в словаре, и доступ к ним будет обеспечиваться соответствующими дескрипторами в классе.И, наконец, несогласованность, которая
A.__dict__
отличается от,A.__dict__['__dict__']
заключается в том, что атрибут__dict__
, в порядке исключения, никогда не просматриваетсяvars(A)
, поэтому то, что верно для него, неверно практически для любого другого атрибута, который вы бы использовали. Например,A.__weakref__
это то же самое, что иA.__dict__['__weakref__']
. Если бы этой несоответствия не было, использованиеA.__dict__
не сработало бы, и вам пришлось бы всегда использоватьvars(A)
вместо него.источник
__dict__
атрибут объекта не может быть сохранен в объекте__dict__
?__dict__
он предназначен для хранения всех атрибутов экземпляра, доступ к атрибуту формыobj.x
в конечном итоге просматривается по объекту__dict__
, а именноobj.__dict__['x']
. Теперь, если бы__dict__
он не был реализован как дескриптор, это привело бы к бесконечной рекурсии, поскольку для доступаobj.__dict__
вам нужно было бы искать его какobj.__dict__['__dict__']
. Дескриптор позволяет обойти эту проблему.Поскольку
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'
источник
A.__dict__['__dict__']
не является ссылкой наA.__dict__
. Он реализует__dict__
атрибут экземпляров. Чтобы попробовать это на себе,A.__dict__['__dict__'].__get__(A(), A)
возвращает атрибутыA()
, покаA.__dict__['__dict__'].__get__(A, type)
не удается.Давайте исследуем!
>>> 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__
.источник
__objclass__
это класс, который определил этот атрибут, а не атрибут этого класса. Это делает вашgetattr
пример неверным. Более правильным было быgetattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
KeyError: '__dict__'
, в отличие от @ AndrewClark.Вы можете попробовать следующий простой пример, чтобы понять это больше:
>>> 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
источник
is
заменяется на==
во втором сравнении, т.е.A.__dict__ is type.__dict__['__dict__'].__get__(A)
результат естьFalse
как в python 2.7.15+, так и в 3.6.8.