【问题标题】:TypeError: __class__ assignment only supported for heap types or ModuleType subclassesTypeError: __class__ 分配仅支持堆类型或 ModuleType 子类
【发布时间】:2018-09-18 00:16:33
【问题描述】:

我正在尝试将任意“基”类中的函数复制到我的新对象中。但是,此示例代码出现以下错误。

class my_base:
    def print_hey():
        print("HEY")

    def get_one():
        print(1)

class my_ext:
    def __init__(self, base):
        methods = [method for method in dir(base) if callable(getattr(base, method))]
        for method in methods:
            setattr(self, method, getattr(base, method))


me = my_ext(my_base)
me.get_one()

上面在调用setattr时得到这个错误。

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

如果我在定义上述内容后将其键入提示符,则该语句有效。

【问题讨论】:

  • 看来 getattr() 返回的所有属性都被认为是可调用的。所以第一个__class__ 是导致此错误的原因。也许还有其他方法可以判断一个属性是否是一个函数?
  • 你为什么要这样做?为什么不继承 my_base?
  • 即使我们修复了 TypeError,代码仍然无法工作,因为您正在向实例添加未绑定的方法。真的,你为什么要这样做?这看起来不像是干净的代码。
  • 在这个设计中有充分的理由这样做。这不是真正的问题。
  • @Aran-Fey 向这样的实例添加方法完全没问题。这是一种动态语言,并且动态发生。

标签: python setattr


【解决方案1】:

您的实例对象具有许多不应重新分配的属性,并且大多数都通过了callable 测试:

>>> [item for item in dir(my_base) if callable(getattr(my_base, item))]
['__class__', '__delattr__', '__dir__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'get_one', 'print_hey']

您应该限制其中的大多数。您可以简单地检查item.startswith('__'),但我不确定您想对__repr__ 和朋友做什么。也许在白名单之后检查下划线?

【讨论】:

  • 是的!而__class__ 原来是真正的问题。
  • 原来我的实际解决方案中的函数都以相同的前缀开头。所以startswith 是正确的解决方案。
  • 太棒了!我打算提出这个建议。
【解决方案2】:

原来__class__ 是可调用的,并且还有许多其他可调用对象。

我只想要这些功能,所以以下工作:

 import types
 . . . 
  methods = [method for method in dir(base) if isinstance(getattr(base, method), types.FunctionType)]
 . . .

【讨论】:

  • 这会捕获其他你不想要的东西,比如__repl__
【解决方案3】:

这里的问题是python中的所有对象都有一个__class__属性来存储对象的类型:

>>> my_base.__class__
<class 'type'>
>>> type(my_base)
<class 'type'>

由于调用类是您创建该类实例的方式,因此它们被视为可调用对象并通过callable 测试:

>>> callable(my_base)
True
>>> my_base()
<__main__.my_base object at 0x7f2ea5304208>

当您的代码尝试将某些内容分配给 __class__ 属性时,您观察到的 TypeError 会被抛出:

>>> object().__class__ = int
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __class__ assignment only supported for heap types or ModuleType subclasses

因此,您需要更具体地了解应该复制哪些属性。

您可以过滤掉带有双下划线的属性:

methods = [method for method in dir(base) if not method.startswith('__')
                                             and callable(getattr(base, method))]

或者你可以过滤掉类:

methods = [method for method in dir(base) if callable(getattr(base, method)) and
                                     not isinstance(getattr(base, method), type)]

或者你只能通过比较types.FunctionType来允许函数:

methods = [method for method in dir(base) if callable(getattr(base, method)) and
                           isinstance(getattr(base, method), types.FunctionType)]

【讨论】:

    猜你喜欢
    • 2016-10-12
    • 2021-10-31
    • 2012-04-09
    • 1970-01-01
    • 2020-12-16
    • 2020-03-06
    • 1970-01-01
    相关资源
    最近更新 更多