CPython在Objects / typeobject.c中对此主题发表了评论:
在3.5之前的CPython版本中,compatible_for_assignment未设置其中的代码
来正确检查非HEAPTYPE类的内存布局/插槽等。因此,__class__在任何不是HEAPTYPE-> HEAPTYPE的情况下,我们都不允许分配。
在3.5开发周期中,我们修复了代码,
compatible_for_assignment以正确检查任意类型之间的兼容性,并开始__class__在所有新旧类型确实具有兼容插槽和内存布局的情况下进行赋值(无论它们是否实现为HEAPTYPEs)或不)。
但是,在3.5发布之前,我们发现这导致了不可变类型(如int)的问题,其中解释器假定它们是不可变的,并且插入了一些值。以前这不是问题,因为它们确实是不可变的-特别是,解释器应用此内部技巧的所有类型也都被静态分配了,因此旧的HEAPTYPE规则“偶然地”阻止了它们进行__class__分配。但是随着__class__分配的变化,我们开始允许像
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
(请参阅https://bugs.python.org/issue24912)。
从理论上讲,正确的解决方法是识别哪些类依赖于此不变性,并__class__通过某种机制(例如,新的Py_TPFLAGS_IMMUTABLE标志(“黑名单”方法))以某种方式禁止对其进行分配。但是实际上,由于在3.5 RC周期中并未注意到此问题,因此我们采用保守的方法,并恢复了以前使用的相同的HEAPTYPE-> HEAPTYPE检查,以及“白名单”。目前,白名单仅由ModuleType子类型组成,因为这些情况首先是导致补丁的原因-请参阅https://bugs.python.org/issue22986-并且由于模块对象是可变的,因此我们可以确定他们绝对没有被拘留。所以现在我们允许HEAPTYPE-> HEAPTYPE 或
ModuleType子类型-> ModuleType子类型。
据我们所知,以下“ if”语句之后的所有代码都将正确处理非HEAPTYPE类,而HEAPTYPE检查仅用于保护解释器为之准备的非HEAPTYPE类的子集。所有实例都是真正不变的。
说明:
CPython以两种方式存储对象:
对象是在堆上分配的结构。特殊规则适用于对象的使用,以确保正确地对其进行垃圾回收。对象永远不会静态分配或在堆栈上分配;它们只能通过特殊的宏和函数进行访问。 (类型对象是第一个规则的例外;标准类型由静态初始化的类型对象表示,尽管对Python 2.2进行类型/类统一的工作也使得也可以具有堆分配的类型对象)。
来自Include / object.h中的注释的信息。
当您尝试将新值设置为时some_obj.__class__,将object_set_class调用该函数。它是从PyBaseObject_Type继承的,请参见/* tp_getset */field。此功能检查:新类型可以替换旧类型some_obj吗?
举个例子:
class A:
pass
class B:
pass
o = object()
a = A()
b = B()
第一种情况:
a.__class__ = B
a对象的类型是A堆类型,因为它是动态分配的。以及B。该a的类型是没有问题的改变。
第二种情况:
o.__class__ = B
的类型o是内置类型object(PyBaseObject_Type)。它不是堆类型,所以TypeError引发了:
TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.