CPython ma komentarz w Objects / typeobject.c na ten temat:
W wersjach CPython wcześniejszych niż 3.5 kod w
compatible_for_assignmentnie został skonfigurowany do prawidłowego sprawdzania zgodności układu / gniazda pamięci itp. Dla klas innych niż HEAPTYPE, więc po prostu zabroniliśmy __class__przypisywania w każdym przypadku, który nie był HEAPTYPE -> HEAPTYPE.
Podczas cyklu programowania 3.5 naprawiliśmy kod,
compatible_for_assignmentaby poprawnie sprawdzać zgodność między dowolnymi typami, i zaczęliśmy zezwalać na __class__przypisywanie we wszystkich przypadkach, w których stare i nowe typy faktycznie miały zgodne gniazda i układ pamięci (niezależnie od tego, czy zostały zaimplementowane jako HEAPTYPE albo nie).
Jednak tuż przed wydaniem 3.5 odkryliśmy, że doprowadziło to do problemów z niezmiennymi typami, takimi jak int, gdzie interpreter zakłada, że są niezmienne i internalizuje niektóre wartości. Dawniej nie był to problem, ponieważ były one naprawdę niezmienne - w szczególności wszystkie typy, w których interpreter stosował tę sztuczkę internowania, również były przydzielane statycznie, więc stare reguły HEAPTYPE „przypadkowo” uniemożliwiały im __class__przypisanie. Ale wraz ze zmianami __class__przypisania zaczęliśmy zezwalać na kod podobny
class MyInt(int):
# ...
# Modifies the type of *all* instances of 1 in the whole program,
# including future instances (!), because the 1 object is interned.
(1).__class__ = MyInt
(patrz https://bugs.python.org/issue24912 ).
Teoretycznie poprawną poprawką byłoby określenie, które klasy polegają na tym niezmienniku i w jakiś sposób zabraniają __class__przypisywania tylko dla nich, być może za pomocą jakiegoś mechanizmu, takiego jak nowa flaga Py_TPFLAGS_IMMUTABLE (podejście „czarnej listy”). Ale w praktyce, ponieważ problem ten nie został zauważony w późnej fazie 3.5 RC, zachowujemy ostrożne podejście i przywracamy to samo sprawdzenie HEAPTYPE-> HEAPTYPE, które mieliśmy wcześniej, oraz „białą listę”. Na razie biała lista składa się tylko z podtypów ModuleType, ponieważ są to przypadki, które przede wszystkim motywowały łatkę - patrz https://bugs.python.org/issue22986 - a ponieważ obiekty modułów są zmienne, możemy być pewni że zdecydowanie nie są internowani. Teraz pozwalamy HEAPTYPE-> HEAPTYPE lub
Podtyp ModuleType -> Podtyp ModuleType.
O ile wiemy, cały kod poza poniższą instrukcją „if” będzie poprawnie obsługiwał klasy inne niż HEAPTYPE, a kontrola HEAPTYPE jest potrzebna tylko w celu ochrony tego podzbioru klas innych niż HEAPTYPE, dla których interpreter przygotował założenie, że wszystkie instancje są naprawdę niezmienne.
Wyjaśnienie:
CPython przechowuje obiekty na dwa sposoby:
Obiekty są strukturami przydzielonymi na stercie. Specjalne zasady mają zastosowanie do korzystania z obiektów, aby zapewnić, że są one odpowiednio odśmiecane. Obiekty nigdy nie są przydzielane statycznie ani na stosie; dostęp do nich można uzyskać tylko poprzez specjalne makra i funkcje. (Obiekty typu są wyjątkami od pierwszej reguły; typy standardowe są reprezentowane przez statycznie inicjowane obiekty typu, chociaż praca nad ujednoliceniem typu / klasy dla Pythona 2.2 umożliwiła również posiadanie obiektów typu przydzielanych na stercie).
Informacje z komentarza w Include / object.h .
Gdy próbujesz ustawić nową wartość some_obj.__class__, object_set_classwywoływana jest funkcja. Jest dziedziczony z PyBaseObject_Type , patrz /* tp_getset */pole. Ta funkcja sprawdza : czy nowy typ może zastąpić stary typ some_obj?
Weź przykład:
class A:
pass
class B:
pass
o = object()
a = A()
b = B()
Pierwszy przypadek:
a.__class__ = B
Typem aobiektu jest Atyp sterty, ponieważ jest on przydzielany dynamicznie. Jak również B. W a„s typ jest zmieniane bez problemu.
Drugi przypadek:
o.__class__ = B
Typ ojest typem wbudowanym object( PyBaseObject_Type). To nie jest typ sterty, więc TypeErrorjest podnoszony:
TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.