Ответ 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)
.