TypeError: super(type, obj): obj must be an instance or subtype of type. Error when calling super after metaclass creation
TypeError: super(type, obj): obj must be an instance or subtype of type. Error when calling super after metaclass creation
假设我使用了一个库,其源代码是按照这种形式编写的:
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
为什么?
您可能不需要自定义元数据class;相反,只需将参数调整为 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
在决定接下来使用哪个 class 时进一步开始 MRO。
% python3 tmp.py
At CustomDemo __init__ method: This message should appear
这个 解决了我的问题。
对于我的情况,在 __new__
方法签名上为 bases
参数更改 basis.__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
元类方法。
假设我使用了一个库,其源代码是按照这种形式编写的:
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
为什么?
您可能不需要自定义元数据class;相反,只需将参数调整为 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
在决定接下来使用哪个 class 时进一步开始 MRO。
% python3 tmp.py
At CustomDemo __init__ method: This message should appear
这个 __new__
方法签名上为 bases
参数更改 basis.__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
元类方法。