覆盖 __class__ 引发 TypeError

Overwriting __class__ raises TypeError

我正在尝试扩展用 C 编写的模块 class 的功能,方法是从 class 派生并覆盖/添加某些方法*。

问题是他的模块创建了 class 我试图在不同地方扩展的实例。因此,我需要创建一个转换方法,将模块提供的基础 class 转换为我自己的具有附加功能的派生 class。

这是我尝试过的方法:

class Derived(Base):
    @classmethod
    def cast(cls, obj: Base):
        obj.__class__ = cls

此方法适用于我自己创建的 class 层次结构 - 但是它在我使用的模块中失败,抛出以下异常:

TypeError: __class__ assignment only supported for heap types or ModuleType subclasses

我很难找到有关此异常的官方信息。任何信息都有帮助,我什至会接受 hacky 解决方案,只要它们是干净的并且完全模仿我正在寻找的行为。

* 我要扩展的 class 是包 pygame.

内的 Surface

__class__ 属性在可接受的范围内一直受到限制,Python 类 可以比 C 中定义的类型灵活得多。

例如,__slots__ section in the datamodel documentation 状态:

__class__ assignment works only if both classes have the same __slots__.

并且同一文档调用 instance.__class__ attribute 只读:

The implementation adds a few special read-only attributes to several object types, where they are relevant.

所以__class__其实本来就不是可写的,但是已经用了很长时间了,非常有用属性。

现在,在您的情况下,不允许分配,因为目标实例的类型不会在堆上分配对象(进程内存区域会增长以容纳任意数量的对象,它是分配 most Python 对象的地方)。未在堆上分配的对象的管理方式不同(例如,不受引用计数的影响),如果您更改了它们的类型,它们将突然需要以不同方式进行管理。目前不支持。

在 Python 3.5 候选版本中是 briefly supported,允许在 模块 上设置 __class__,但一旦它被适得其反发现可以改变内部不可变值的类型:

class MyInt(int):
    # lets make ints mutable!

(1).__class__ = MyInt

1 是低价的 interned value, so now all use of the integer 1 everywhere in the Python program has been changed. Not what you want, especially if you are re-using interpreter memory across multiple processes the way the Google App Engine does! See issue #24912

但这就是异常中特别提到模块实例的原因,参见Customising module attribute access

您必须找到其他途径来解决您的问题。例如,也许您的特定问题可以通过 包装 某些使用 __getattr__ 将属性代理到包装实例的实例来解决。