python元类解析器

f3temu5u  于 2021-09-08  发布在  Java
关注(0)|答案(1)|浏览(470)

上下文

我开始用python探索元类的概念。很快,我发现自己面临着一个共同的问题。 TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases 根据我的理解,当您创建一个继承自两个(或更多)类[a,b]的类[c]时,就会发生这种情况,而这两个类不共享同一个元类[m_a,m_b]

M_A     M_B
 :       :
 :       :
 A       B
  \     /
   \   /
     C

这里很好地描述了这个问题,解决方案也很简单。我们需要创建一个新的元类[m_c],它继承自m_a和m_b
我试图通过创建一个动态创建[m_c]的方法,使这个过程自动化。像这样的

我的问题

我的类[c]继承自[b],我想使用[m_a]有它的元类,
m_a是一个自定义元类(singleton)
b的元类是.meta
我的 metaclass_resolver() 成功创建新元类[m_c]
但是它继承自 .ABCMetatype 而不是继承自 .ABCMetaM_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' 谢谢

xn1cxnb4

xn1cxnb41#

你应该只使用 new_metaclass = type(new_meta_name, metaclasses, {}) 永远不会 new_metaclass(new_meta_name, metaclasses, {}) <-通过这种安排,您正在更改元类的元类-并且您希望元类的元类始终保持不变 type 如果您只想组合这两个元类。
很可能是由于.meta的意外输入,甚至是您自己的输入 M_A 类,你会得到只有一个祖先元类的副作用。这可能是你意外行为的根源。
换言之:要创建元类本身,请调用type,而不是您正在组合的元类中的任何一个,即使它碰巧起作用,对于普通类来说,使这些元类成为“元类”的机制可能并不有用,而且对于作为其他组合元类的元类(包括自身作为祖先)来说,肯定毫无意义。
另一件事是通过使用 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()), {})

相关问题