2016-08-26 2 views
5
>>> class D: 
...  __class__ = 1 
...  __name__ = 2 
... 
>>> D.__class__ 
<class 'type'> 
>>> D().__class__ 
1 
>>> D.__name__ 
'D' 
>>> D().__name__ 
2 

Почему D.__class__ возвращает имя класса, в то время как D().__class__ возвращает определенный атрибут в классе D?правило поиска атрибутов класса?

И откуда взялись такие встроенные атрибуты, как __class__ и __name__?

Я подозревал __name__ или __class__ быть простыми дескрипторы, которые живут либо в object классе или где-нибудь, но это не видно.

В моем понимании, это правило атрибута поиска следующим образом в Python, опуская условия для дескрипторов и т.д ..:

Instance --> Class --> Class.__bases__ and the bases of the other classes as well

Учитывая тот факт, что класс является экземпляром метакласса, type в этот случай, почему D.__class__ не ищет __class__ в D.__dict__?

ответ

8

Имена __class__ и __name__ являются специальными. Оба являются дескрипторы данных. __name__ определяется на type объекта, __class__ определяется на object (базовый-класс всех новых классов):

>>> type.__dict__['__name__'] 
<attribute '__name__' of 'type' objects> 
>>> type.__dict__['__name__'].__get__ 
<method-wrapper '__get__' of getset_descriptor object at 0x1059ea870> 
>>> type.__dict__['__name__'].__set__ 
<method-wrapper '__set__' of getset_descriptor object at 0x1059ea870> 
>>> object.__dict__['__class__'] 
<attribute '__class__' of 'object' objects> 
>>> object.__dict__['__class__'].__get__ 
<method-wrapper '__get__' of getset_descriptor object at 0x1059ea2d0> 
>>> object.__dict__['__class__'].__set__ 
<method-wrapper '__set__' of getset_descriptor object at 0x1059ea2d0> 

Поскольку они являются дескрипторы данных, то type.__getattribute__ method (используется для доступа к атрибутам по классу) будет игнорировать любые атрибуты, заданные в классе __dict__ и использовать только дескрипторы себя:

>>> type.__getattribute__(Foo, '__class__') 
<class 'type'> 
>>> type.__getattribute__(Foo, '__name__') 
'Foo' 

Забавный факт: type происходит от object (все в Python является объектом), который является, почему __class__ находится на type при проверке данных дескрипторов:

>>> type.__mro__ 
(<class 'type'>, <class 'object'>) 

(type.__getattribute__(D, ...) используется непосредственно в качестве несвязанного метода, не D.__getattribute__(), потому что all special method access goes to the type).

См Descriptor Howto собой, что представляет дескриптор данных и почему это имеет значение:

Если объект определяет как __get__() и __set__(), он считается дескриптором данных. Дескрипторы, которые определяют только __get__(), называются дескрипторами без данных (они обычно используются для методов, но возможны другие виды использования).

Дескрипторы данных и данных, отличные от того, как вычисляются переопределения в отношении записей в словаре экземпляра. Если словарь экземпляра имеет запись с тем же именем, что и дескриптор данных, дескриптор данных имеет приоритет. Если словарь экземпляра имеет запись с тем же именем, что и дескриптор не данных, запись словаря имеет приоритет.

Для дескрипторов данных на type класс является еще одним экземпляром.

Так, глядя вверх __class__ или __name__ атрибутов, это не имеет значения, что определенно в D.__dict__ имен, потому как дескриптор данных находится в пространстве имен, образованных type и это MRO.

Эти дескрипторы определены в typeobject.c C code:

static PyGetSetDef type_getsets[] = { 
    {"__name__", (getter)type_name, (setter)type_set_name, NULL}, 
    /* ... several more ... */ 
} 

/* ... */ 

PyTypeObject PyType_Type = { 
    PyVarObject_HEAD_INIT(&PyType_Type, 0) 
    "type",          /* tp_name */ 
    /* ... many type definition entries ... */ 
    type_getsets,        /* tp_getset */ 
    /* ... many type definition entries ... */ 
} 

/* ... */ 

static PyGetSetDef object_getsets[] = { 
    {"__class__", object_get_class, object_set_class, 
    PyDoc_STR("the object's class")}, 
    {0} 
}; 

PyTypeObject PyBaseObject_Type = { 
    PyVarObject_HEAD_INIT(&PyType_Type, 0) 
    "object",         /* tp_name */ 
    /* ... many type definition entries ... */ 
    object_getsets,        /* tp_getset */ 
    /* ... many type definition entries ... */ 
} 

В случаях используется object.__getattribute__, и он будет найти __name__ и __class__ записи в D.__dict__ отображения, прежде чем он будет найти дескрипторы данных на object или type.

Если опустить либо, однако, то, глядя на имена на D() только __class__ в качестве дескриптора данных в МРО D (так, на object). __name__ не найден, поскольку метатипы не учитываются при разрешении атрибутов экземпляра.

Таким образом вы можете установить __name__ на экземпляре, но не __class__:

>>> class E: pass 
... 
>>> e = E() 
>>> e.__class__ 
<class '__main__.E'> 
>>> e.__name__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'E' object has no attribute '__name__' 
>>> e.__dict__['__class__'] = 'ignored' 
>>> e.__class__ 
<class '__main__.E'> 
>>> e.__name__ = 'this just works' 
>>> e.__name__ 
'this just works' 
+0

я получаю большинство частей вашего ответа и проверить, что: правила для атрибута поиска различается для классов и экземпляров. Я вижу «тип .__ getattribute__» дает более высокий приоритет дескрипторам данных, такой же, как 'object .__ getattribute__'. Но оба типа .__ getattribute__' и 'object .__ getattribute__' проверяют тип объекта. Если это экземпляр класса, поиск '__dict__' выполняется перед дескрипторами данных. Это правильно? – direprobs

+0

@ direprobs: происходит то, что вы начинаете поиск атрибута на объекте, а Python всегда выполняет 'type (object) .__ getattribute __ (object, attribute_name)'; для объектов класса, которые являются 'type .__ getattribute__', для экземпляров, которые являются' object .__ getattribute__' (в обоих случаях, если они не переопределены подтипом/подклассом). Таким образом, «проверка типа» имеет место * до того, как * '__getattribute__' даже называется. –

+0

Да, это так. Дело в том, что я попробовал 'type .__ getattribute __ (A, '__class __')', который возвращает ''. Таким образом, оба типа: .__ getattribute__ и 'object .__ getattribute__' рассматривают дескрипторы данных с более высоким приоритетом, чем' __dict__' для (классы), а не (экземпляры). Но когда я запускал 'type .__ getattribute __ (A(), '__class __')', это возвращает '1'. Это говорит о том, что существует некоторый тип проверки типов (будь то объект, переданный в '__getattribute__', является классом или экземпляром класса), если это класс, тогда Python будет сначала искать дескрипторы данных. – direprobs