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

Dlaczego D.__class__ zwróci nazwę klasy, natomiast D().__class__ zwraca określony atrybut w klasie D?Reguła wyszukiwania atrybutów klas?

A skąd pochodzą atrybuty wbudowane, takie jak __class__ i __name__?

Podejrzewałem __name__ lub __class__ być proste deskryptory, które żyją albo w object klasy lub gdzieś, ale tego nie widać.

W moim rozumieniu, zasada odnośnika atrybut następująco w Pythonie, pomijając warunki deskryptorów itp ..:

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

Biorąc pod uwagę fakt, że klasa jest instancją metaklasą, type w w tym przypadku, dlaczego D.__class__ nie szuka __class__ w D.__dict__?

Odpowiedz

8

Nazwy __class__ i __name__ są specjalne. Oba są deskryptorami danych . __name__ jest zdefiniowana w obiekcie type, __class__ jest zdefiniowana na object (baza-klasa wszystkich klas w nowym stylu):

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

Ponieważ są deskryptory danych, type.__getattribute__ method (używany do dostępu atrybut klasy) zignoruje wszelkie atrybuty określone w klasie __dict__ i używać tylko deskryptory się:

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

Ciekawostka: type wywodzi object (wszystko Pythona Przedmiotem), dlatego __class__ znajduje się na type przy sprawdzaniu danych deskryptorów:

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

(type.__getattribute__(D, ...) stosuje się bezpośrednio w postaci niezwiązanej metodzie nie D.__getattribute__(), ponieważ all special method access goes to the type).

Zobacz Descriptor Howto o co stanowi deskryptor danych i dlaczego to się liczy:

Jeśli obiekt definiuje zarówno __get__() i __set__(), uważa się deskryptor danych. Deskryptory, które definiują tylko __get__() nazywane są deskryptorami niebędącymi danymi (są zwykle używane do metod, ale możliwe są inne zastosowania).

Deskryptory danych i inne niż dane różnią się sposobem obliczania przesłonięć w odniesieniu do wpisów w słowniku instancji. Jeśli słownik instancji ma pozycję o takiej samej nazwie jak deskryptor danych, pierwszeństwo ma deskryptor danych. Jeśli słownik instancji ma pozycję o takiej samej nazwie jak deskryptor niebędący deskryptorem, pierwszeństwo ma pozycja w słowniku.

Dla deskryptorów danych na type, klasa jest po prostu kolejną instancją.

Więc kiedy patrzy się na __class__ lub __name__ atrybuty, to nie ma znaczenia, co jest zdefiniowane w przestrzeni nazw D.__dict__, bo albo deskryptor dane znajdują się w przestrzeni nazw utworzonym przez type i to MRO.

te deskryptory są definiowane w 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 ... */ 
} 

W przypadkach object.__getattribute__ służy, i będzie ona znaleźć __name__ i __class__ wpisy w mapowaniu D.__dict__ zanim będzie ona znaleźć deskryptorów danych na object lub type.

Jeśli pominąć albo jednak potem patrząc na nazwiska D() tylko __class__ jako deskryptor danych w MRO z D (tak, to na object). __name__ nie znaleziono, ponieważ metatypy nie są uwzględniane podczas rozwiązywania atrybutów instancji.

Jako takie można ustawić __name__ na przykład, ale nie __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

uzyskać większość części odpowiedź i przetestować, że: przepisy dla odnośnika atrybutu różni się dla klas i instancji. Widzę, że 'type .__ getattribute__' nadaje deskryptorom danych wyższy priorytet, taki sam jak' object .__ getattribute__'. Ale zarówno 'type .__ getattribute__', jak i' object .__ getattribute__' sprawdzają typ obiektu. Jeśli jest to instancja klasy, '__dict__' jest przeszukiwany przed deskryptorami danych. Czy to prawda? – direprobs

+0

@direprobs: co się dzieje, to rozpoczęcie wyszukiwania atrybutów na obiekcie, a Python zawsze robi 'type (object) .__ getattribute __ (object, attribute_name)'; dla obiektów klasy "typ .__ getattribute__", dla instancji "obiekt .__ getattribute__" (w obu przypadkach, o ile nie jest zdefiniowany ponownie przez podtyp/podklasę). Tak więc "sprawdzanie typu" odbywa się * przed wywołaniem * '__getattribute__'. –

+0

Tak, zgadza się. Chodzi o to, próbowałem 'type .__ getattribute __ (A, '__class __')' który zwraca ''. Jako takie, zarówno 'type .__ getattribute__' jak i' object .__ getattribute__' uważają deskryptory danych o wyższym priorytecie niż '__dict__' dla (Classes), a nie (Instances). Ale kiedy uruchomiłem 'type .__ getattribute __ (A(), '__class __')' to zwraca '1'. Sugeruje to, że istnieje pewnego rodzaju sprawdzanie typu (czy obiekt przekazywany do '__getattribute__' jest klasą lub wystąpieniem klasy), jeśli jest to klasa, to Python najpierw szuka deskryptorów danych. – direprobs