class A(object):
pass
class B(A):
pass
o = object()
a = A()
b = B()
Chociaż mogę się zmienić a.__class__
, nie mogę zrobić tego samego z o.__class__
(generuje TypeError
błąd). Dlaczego?
Na przykład:
isinstance(a, A) # True
isinstance(a, B) # False
a.__class__ = B
isinstance(a, A) # True
isinstance(a, B) # True
isinstance(o, object) # True
isinstance(o, A) # False
o.__class__ = A # This fails and throws a TypeError
# isinstance(o, object)
# isinstance(o, A)
Wiem, że na ogół nie jest to dobry pomysł, ponieważ może prowadzić do bardzo dziwnych zachowań, jeśli zostanie źle potraktowany. To tylko ze względu na ciekawość.
python
python-3.x
Riccardo Bucco
źródło
źródło
Odpowiedzi:
CPython ma komentarz w Objects / typeobject.c na ten temat:
Wyjaśnienie:
CPython przechowuje obiekty na dwa sposoby:
Informacje z komentarza w Include / object.h .
Gdy próbujesz ustawić nową wartość
some_obj.__class__
,object_set_class
wywoływana jest funkcja. Jest dziedziczony z PyBaseObject_Type , patrz/* tp_getset */
pole. Ta funkcja sprawdza : czy nowy typ może zastąpić stary typsome_obj
?Weź przykład:
Pierwszy przypadek:
Typem
a
obiektu jestA
typ sterty, ponieważ jest on przydzielany dynamicznie. Jak równieżB
. Wa
„s typ jest zmieniane bez problemu.Drugi przypadek:
Typ
o
jest typem wbudowanymobject
(PyBaseObject_Type
). To nie jest typ sterty, więcTypeError
jest podnoszony:źródło
Możesz zmienić tylko
__class__
na inny typ, który ma ten sam układ wewnętrzny (C) . Środowisko wykonawcze nawet nie zna tego układu, chyba że sam typ jest dynamicznie przydzielany („typ sterty”), więc jest to warunek konieczny, który wyklucza wbudowane typy jako źródło lub miejsce docelowe. Musisz także mieć ten sam zestaw o__slots__
takich samych nazwach.źródło