上下文
我开始用python探索Metaclass的概念。很快,我发现自己面临着一个共同的问题。
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
据我理解,当您创建一个类C,它继承两个类(或更多的) A,B不共享相同的元类M_A,M_B<代码>E 210时,就会发生这种情况。
M_A M_B
: :
: :
A B
\ /
\ /
C这个问题描述得很好,这里的解决方案很简单。我们需要创建一个新的元类M_C,它继承了M_A和M_B。
我试图通过创建一个动态创建M_C的方法来使此过程自动化。有点像这
我的问题
我的类C继承了B,我想使用M_A有它的元类,
M_A是一个自定义元类(单例)
B的元类是abc.ABCMeta
我的metaclass_resolver()成功地创建了一个新的元类M_C
然而,它继承的是abc.ABCMeta和type,而不是从abc.ABCMeta和M_A继承的。
from collections import UserDict
def metaclass_resolver(*classes):
metaclasses = tuple(set(type(cls) for cls in classes))
new_metaclass = metaclasses[0]
new_meta_name = new_metaclass.__name__
#if there's more than one metaclass
#combine them and create a new metaclass (M_C)
if len(metaclasses) > 1:
#get the name of each metaclass
new_meta_name = "_".join(mcls.__name__ for mcls in metaclasses)
#create a new dynamic class
# type('name','bases {inheritance}, attrs)'
new_metaclass = type(new_meta_name, metaclasses, {})
return new_metaclass(new_meta_name, metaclasses, {})
#my custom metaclass (singleton)
class M_A(type):
def __new__(cls, name, bases, attrs):
c = super().__new__(cls, name, bases, attrs)
return c
def __init__(cls, name, bases, attrs):
#When this metaclass is initiated, no instance of the class
#using this metaclass would have been created
cls.__instance = None
super().__init__(name, bases, attrs)
def __call__(cls, *args, **kwargs):
#get the saved instance
instance = cls.__instance
#if the instance does not exists
if instance is None:
#create one
instance = cls.__new__(cls)
instance.__init__(*args, **kwargs)
#save it
cls.__instance = instance
return instance
pass
#abc metaclass
class B(UserDict):
def method_needed_in_C(self):
pass
#my class
class C(B, metaclass = metaclass_resolver(M_A, B)):
def __init__(self):
super().__init__()
print(type(self.__class__))
#<class 'abc.ABCMeta_type'>
pass
if __name__ == "__main__":
c = C()在metaclass_resolver()中,当我使用type(cls)时,它返回<class 'type'>而不是<class 'M_A'>,这是有意义的,因为每个类都是从type派生的。但是,我怎样才能直接指向<class 'M_A'>
如果我直接使用cls,就会得到以下错误:TypeError: descriptor '__init__' requires a 'type' object but received a 'str'
谢谢!
发布于 2021-07-09 20:55:16
你只应该用
new_metaclass = type(new_meta_name, metaclasses, {})而非new_metaclass(new_meta_name, metaclasses, {}) <- -通过这种安排,您将更改元类的元类,如果您只想将这两种元类组合起来,则希望元类的元类始终是type。
很可能在abc.ABCMeta的意外输入上,甚至在您自己的M_A类上,yo得到了只有一个祖先元类的副作用。这可能是你意外行为的根源。
换句话说:要创建你的元类本身,调用类型而不是你要组合的元类之一--即使它恰好是有效的,使这些元类作为普通类的“元类”有用的机制可能是没有用的,而且对于包含自己作为祖先的其他组合元类的元类来说也是完全没有意义的。
另一件事是,通过使用tuple(set(...))来消除重复的元类,您基本上是将它们的顺序随机化:元类是一种敏感的东西,而且并非所有元类都是以一种可以与其他元类组合的方式构建的--而且很少有那么多元类能够以随机顺序组合。您可以使用dict理解,这将保持顺序和消除重复,并可以提取元类名称为您的幻想命名在一个步骤:
def metaclass_resolver(*classes):
metaclasses = {mcls.__name__: mcls for cls in classes if (mcls:=type(cls)) is not type}
new_meta_name = "_".join(metaclasses)
return type(new_meta_name, tuple(metaclasses.values()), {})https://stackoverflow.com/questions/68319872
复制相似问题