我正在尝试通过从C类编写的模块类派生并重写/添加某些方法*来扩展其功能。
问题是他的模块创建了我尝试在各个不同地方扩展的类的实例。因此,我需要创建一个强制转换方法,以将模块提供的基类转换为具有附加功能的我自己的派生类。
这是我尝试过的:
class Derived(Base):
@classmethod
def cast(cls, obj: Base):
obj.__class__ = cls
此方法适用于我自己创建的类层次结构-但是在我使用的模块上失败,并抛出以下异常:
TypeError:__class__赋值仅支持堆类型或ModuleType子类
我很难找到有关此例外的官方信息。任何信息都会有所帮助,只要它们干净并且完全模仿我要寻找的行为,我什至会接受骇人听闻的解决方案。
*我要扩展的类是Surface
包中的pygame
。
答案 0 :(得分:3)
__class__
属性始终受到可接受的限制,并且Python类比C语言中定义的类型灵活得多。
例如,__slots__
section in the datamodel documentation指出:
__class__
分配仅在两个类具有相同的__slots__
时才有效。
,同一文档称为instance.__class__
attribute 只读:
该实现在与它们相关的几种对象类型中添加了一些特殊的只读属性。
所以__class__
实际上不是可写的,但是已经存在很长时间了,它是一个非常有用的属性。
现在,在您的情况下,不允许分配,因为目标实例的类型无法在堆上分配对象(进程内存的区域可以容纳任意数量的对象,在此处分配了 most 个Python对象)。未对堆上未分配的对象进行不同的管理(例如,不进行引用计数),并且如果更改了它们的类型,则突然需要对它们进行不同的管理。目前不支持。
它是Python 3.5版本候选版本中的briefly supported,允许在模块上设置__class__
,但一旦发现可以更改类型,便适得其反内定不变值:
class MyInt(int):
# lets make ints mutable!
(1).__class__ = MyInt
1
是interned value,所以现在在Python程序的所有地方所有使用整数1
都已更改。并非您想要的,尤其是如果您要像Google App Engine那样在多个进程中重复使用解释器内存时!下拉菜单请参见issue #24912。
但这是在异常中特别提到模块实例的原因,请参见Customising module attribute access。
您将不得不找到解决问题的其他途径。例如,也许可以通过在使用__getattr__
的属性中将实例代理到包装实例中的方法来包装实例来解决您的特定问题。