class A(object):
pass
class B(A):
pass
o = object()
a = A()
b = B()
虽然我可以更改a.__class__
,但不能对o.__class__
做同样的操作(它会引发TypeError
错误)。为什么?
例如:
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)
我知道这通常不是一个好主意,因为如果处理不当,可能会导致某些非常奇怪的行为。只是出于好奇。
答案 0 :(得分:6)
CPython 在Objects/typeobject.c中对此主题有评论:
在CPython 3.5之前的版本中,
compatible_for_assignment
未设置为正确检查内存 非HEAPTYPE类的layout / slot / etc。兼容性,因此我们 在任何不是HEAPTYPE的情况下都只是禁止__class__
分配 ->堆类型。在3.5开发周期中,我们将代码固定在
compatible_for_assignment
正确检查两者之间的兼容性 任意类型,并开始允许在所有类型中分配__class__
新旧类型确实具有兼容插槽的情况 和内存布局(无论它们是否实现为 是否为HEAPTYPE)。不过,在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 or ModuleType子类型-> ModuleType子类型。据我们所知,以下“ if”语句之后的所有代码 将正确处理非HEAPTYPE类,并且HEAPTYPE检查为 只需要保护非HEAPTYPE类的子集即可 假设所有实例都是 真正的一成不变。
CPython 以两种方式存储对象:
对象是在堆上分配的结构。特殊规则适用于 使用对象以确保正确地对其进行垃圾回收。 对象永远不会静态分配或在堆栈上分配;他们一定是 只能通过特殊的宏和函数访问。 (类型对象是 第一条规则的例外;标准类型表示为 静态初始化的类型对象,尽管可以处理类型/类 Python 2.2的统一使得可以进行堆分配 也输入对象)。
Include/object.h中评论的信息。
当您尝试将新值设置为some_obj.__class__
时,将调用object_set_class
函数。它是从PyBaseObject_Type继承的,请参见/* tp_getset */
字段。函数checks:新类型可以替换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.
答案 1 :(得分:4)
您只能将__class__
更改为具有相同内部(C)布局的其他类型。除非类型本身是动态分配的(“堆类型”),否则运行时甚至都不知道该布局,因此这是一个必要条件,不能将内置类型作为源或目标。您还必须具有相同名称的相同__slots__
集。