首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Metaclass转换器

Metaclass转换器
EN

Stack Overflow用户
提问于 2021-07-09 16:12:55
回答 1查看 106关注 0票数 1

上下文

我开始用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,它继承两个类(或更多的) AB不共享相同的元类M_AM_B<代码>E 210时,就会发生这种情况。

代码语言:javascript
复制
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.ABCMetatype,而不是从abc.ABCMetaM_A继承的。

代码语言:javascript
复制
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'

谢谢!

EN

回答 1

Stack Overflow用户

发布于 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理解,这将保持顺序和消除重复,并可以提取元类名称为您的幻想命名在一个步骤:

代码语言:javascript
复制
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()), {})
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68319872

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档