CPython tiene un comentario en Objects / typeobject.c sobre este tema:
En las versiones de CPython anteriores a 3.5, el código
compatible_for_assignmentno estaba configurado para verificar correctamente la compatibilidad del diseño de memoria / ranura / etc. para las clases que no son HEAPTYPE, por lo que simplemente rechazamos la __class__asignación en cualquier caso que no fuera HEAPTYPE -> HEAPTYPE.
Durante el ciclo de desarrollo 3.5, arreglamos el código
compatible_for_assignmentpara verificar correctamente la compatibilidad entre tipos arbitrarios, y comenzamos a permitir la __class__asignación en todos los casos en que los tipos antiguos y nuevos tenían ranuras y diseño de memoria compatibles (independientemente de si se implementaron como HEAPTYPE) o no).
Sin embargo, justo antes de que se lanzara 3.5, descubrimos que esto condujo a problemas con tipos inmutables como int, donde el intérprete asume que son inmutables e interna algunos valores. Anteriormente, esto no era un problema, porque realmente eran inmutables, en particular, todos los tipos en los que el intérprete aplicaba este truco interno también estaban asignados estáticamente, por lo que las viejas reglas HEAPTYPE les impedían "accidentalmente" __class__asignarlos. Pero con los cambios en la __class__asignación, comenzamos a permitir códigos como
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
(ver https://bugs.python.org/issue24912 ).
En teoría, la solución adecuada sería identificar qué clases dependen de esta invariante y de alguna manera rechazar la __class__asignación solo para ellas, tal vez a través de algún mecanismo como un nuevo indicador Py_TPFLAGS_IMMUTABLE (un enfoque de "lista negra"). Pero en la práctica, dado que este problema no se notó al final del ciclo 3.5 RC, estamos adoptando el enfoque conservador y restableciendo la misma verificación HEAPTYPE-> HEAPTYPE que teníamos, más una "lista blanca". Por ahora, la lista blanca consta solo de subtipos de ModuleType, ya que esos son los casos que motivaron el parche en primer lugar, consulte https://bugs.python.org/issue22986 , y dado que los objetos del módulo son mutables, podemos estar seguros que definitivamente no están siendo internados. Entonces ahora permitimos HEAPTYPE-> HEAPTYPE o
Subtipo ModuleType -> Subtipo ModuleType.
Hasta donde sabemos, todo el código más allá de la siguiente instrucción 'if' manejará correctamente las clases que no son HEAPTYPE, y la verificación HEAPTYPE es necesaria solo para proteger ese subconjunto de clases que no son HEAPTYPE para las cuales el intérprete ha asumido que Todas las instancias son verdaderamente inmutables.
Explicación:
CPython almacena objetos de dos maneras:
Los objetos son estructuras asignadas en el montón. Se aplican reglas especiales al uso de objetos para garantizar que se recojan correctamente. Los objetos nunca se asignan estáticamente o en la pila; se debe acceder a través de macros especiales y funciones solamente. (Los objetos de tipo son excepciones a la primera regla; los tipos estándar están representados por objetos de tipo inicializados estáticamente, aunque el trabajo en la unificación de tipo / clase para Python 2.2 también hizo posible tener objetos de tipo asignados en el montón).
Información del comentario en Incluir / objeto.h .
Cuando intenta establecer un nuevo valor some_obj.__class__, object_set_classse llama a la función. Se hereda de PyBaseObject_Type , ver /* tp_getset */campo. Esta función comprueba : ¿puede el nuevo tipo reemplazar al antiguo some_obj?
Toma tu ejemplo:
class A:
pass
class B:
pass
o = object()
a = A()
b = B()
Primer caso:
a.__class__ = B
El tipo de aobjeto es A, el tipo de montón, porque se asigna dinámicamente. Así como el B. El atipo de 's se cambia sin problema.
Segundo caso:
o.__class__ = B
El tipo de oes el tipo incorporado object( PyBaseObject_Type). No es de tipo montón, por lo que TypeErrorse genera:
TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.