【问题标题】:TypeError: super(type, obj): obj must be an instance or subtype of type. Error when calling super after metaclass creationTypeError: super(type, obj): obj 必须是类型的实例或子类型。创建元类后调用 super 时出错
【发布时间】:2020-05-18 12:59:00
【问题描述】:

假设我使用了一个库,他们的源代码是按照这个形状编写的:

class SuperLibraryDemo:
    def __init__(self, test) -> None:
        self.test = test

    def demo(self) -> str:
        return "Returning from demo method with name: %s" % self.test


class LibraryDemo(SuperLibraryDemo):
    def __init__(self, test: str = "something") -> None:
        super(LibraryDemo, self).__init__(test)
        print("At LibraryDemo __init__ method: This should have been skipped")

    def demo(self) -> None:
        super().demo()

记住那是一个图书馆。我不应该根据我的需要调整它的源代码。 但是我需要切换__init__ 方法在LibraryDemo 中调用的内部代码,原因超出了这个问题的范围。

考虑到这个目标,我决定在元类的帮助下编写CustomLibraryDemo,如下所示:

class MetaDemo(type):
    def __new__(mcs, class_name: str, bases: Tuple[Type, ...], class_dict: Dict[str, Any]):
        basis = bases[0]
        c_attrs = dict(basis.__dict__)
        prior_c_process_bases = basis.__base__
        c_attrs["__init__"] = lambda self, settings: prior_c_process_bases.__init__(self, settings)
        new_bases = types.new_class(basis.__qualname__, basis.__bases__,
                                    exec_body=lambda np: MetaDemo.populate_class_dict(np, c_attrs))
        return super(MetaDemo, mcs).__new__(mcs, class_name, (new_bases,), class_dict)

    @staticmethod
    def populate_class_dict(namespace: Dict[str, Any], attr: Dict[str, Any]) -> None:
        for key, value in attr.items():
            namespace[key] = value

class CustomLibraryDemo(LibraryDemo, metaclass=MetaDemo):
    def __init__(self, test: Optional[str] = None) -> None:
        super(CustomLibraryDemo, self).__init__(test)
        print("At CustomDemo __init__ method: This message should appear")

    def test(self) -> None:
        print("In test method at CustomLibraryDemo class: %s" % self.test)

虽然这种方法乍一看似乎对我有用,但当我打电话给CustomLibraryDemo().demo() 说:

TypeError: super(type, obj): obj must be an instance or subtype of type

为什么?

【问题讨论】:

    标签: python python-3.x metaprogramming


    【解决方案1】:

    您可能不需要自定义元类;相反,只需将参数调整为super

    class CustomLibraryDemo(LibraryDemo):
        def __init__(self, test: Optional[str] = None) -> None:
            super(LibraryDemo, self).__init__(test)
            print("At CustomDemo __init__ method: This message should appear")
    
        def test(self) -> None:
            print("In test method at CustomLibraryDemo class: %s" % self.test)
    

    使用LibraryDemo 而不是CustomerLibraryDemo 会导致super 在决定接下来使用哪个类时沿着MRO 进一步开始。

    % python3 tmp.py
    At CustomDemo __init__ method: This message should appear
    

    【讨论】:

    • 是的,非常简单的解决方案。即使我的方法可行,它也是一个戏剧性的过度工程解决方案。
    【解决方案2】:

    question 解决了我的问题。 就我而言,在__new__ 方法签名上将basis.__bases__ 更改为bases 参数可以解决问题。 这样,new_bases 变量的语法变成了:

    new_bases = types.new_class(basis.__qualname__, bases,
                                        exec_body=lambda np: MetaDemo.populate_class_dict(np, c_attrs))
    

    顺便说一句,这段代码可以简化为:

    new_bases = type(basis.__qualname__, bases, c_attrs)
    

    删除populate_class_dict 元类方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-30
      • 1970-01-01
      • 2021-03-13
      • 2021-05-12
      • 2021-04-14
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多