来自元类工厂的多重继承

Posted

技术标签:

【中文标题】来自元类工厂的多重继承【英文标题】:Multi inheritance from metaclasses factories 【发布时间】:2012-02-15 06:06:54 【问题描述】:

我希望SuperClass12 继承自SuperClass1SuperClass2

def getClass1(): 
    class MyMetaClass1(type):
        def __new__(cls, name, bases, dct):
            print dct.get("Attr","")+"Meta1"
            return super(MyMetaClass1, cls).__new__(cls, name, bases, dct)
    return MyMetaClass1

def getClass2(): 
    class MyMetaClass2(type):
        def __new__(cls, name, bases, dct):
            print dct.get("Attr","")+"Meta2"
            return super(MyMetaClass2, cls).__new__(cls, name, bases, dct)
    return MyMetaClass2    

class SuperClass1():
    __metaclass__ = getClass1()
    def fun1(self):
        pass

class SuperClass2():
    __metaclass__ = getClass2()
    def fun2(self):
        pass

class MyClass1(SuperClass1):
    Attr = "MC1"

class MyClass2(SuperClass2):
    Attr = "MC2"

def getClass12(): 
    class myMultiMeta(getClass1(),getClass2()):
        pass
    return myMultiMeta

class SuperClass12(SuperClass1,SuperClass2):
#class SuperClass12(): gives no errors in class construction but then 
#fun1() and fun2() are not members of SuperClass12. 
    __metaclass__ = getClass12()

class MyClass12(SuperClass12):
    Attr = "MC12"

Instance = MyClass12()
Instance.fun1()
Instance.fun2()

很遗憾我遇到了这个错误:

TypeError: 调用元类基类时出错元类冲突:派生类的元类必须是其所有基类的元类的(非严格)子类

但我不明白为什么,因为我的派生类SuperClass12 的元类myMultiMeta 确实是其所有基类(SuperClass1,SUperClass2) 的元类(MyMetaClass1,MyMetaClass2) 的子类。

【问题讨论】:

【参考方案1】:

这是你想要的吗?

class MyMetaClass1(type):
    def __new__(cls, name, bases, dct):
        print dct.get("Attr","")+"Meta1"
        return super(MyMetaClass1, cls).__new__(cls, name, bases, dct)

class MyMetaClass2(type):
    def __new__(cls, name, bases, dct):
        print dct.get("Attr","")+"Meta2"
        return super(MyMetaClass2, cls).__new__(cls, name, bases, dct)

class SuperClass1():
    __metaclass__ = MyMetaClass1
    def fun1(self):
        pass

class SuperClass2():
    __metaclass__ = MyMetaClass2
    def fun2(self):
        pass

class MyClass1(SuperClass1):
    Attr = "MC1"

class MyClass2(SuperClass2):
    Attr = "MC2"

class MyMultiMeta(MyMetaClass1, MyMetaClass2):
    pass

class SuperClass12(SuperClass1, SuperClass2):
#class SuperClass12(): gives no errors in class construction but then 
#fun1() and fun2() are not members of SuperClass12. 
    __metaclass__ = MyMultiMeta

class MyClass12(SuperClass12):
    Attr = "MC12"

Instance = MyClass12()
Instance.fun1()
Instance.fun2()

运行:

vic@ubuntu:~/Desktop$ python test.py 
Meta1
Meta2
MC1Meta1
MC2Meta2
Meta1
Meta2
MC12Meta1
MC12Meta2
vic@ubuntu:~/Desktop$ 

【讨论】:

是的,这是所需的输出,现在我想知道为什么有些人定义返回元类的函数,就像我尝试使用 getClass1()、getClass2() 和 getClass12() 一样?我的代码有什么问题? @jimifiki,坦率地说 - 我不知道 :)。我只是试图清除你的这些函数的代码。【参考方案2】:

请记住,getClass1getClass2 返回元类的新实例。因此,当您设置__metaclass__ = getClass1() 时,它与myMultiMeta 继承的元类不同。

【讨论】:

是的,但是“元类工厂”对于多继承来说是一个糟糕的解决方案。 “元类工厂”有什么用? 这对很多事情都有好处,但是由于已经讨论过的问题,堆叠工厂不能以您的方式完成。更好的选择是将它变成一个可以抓取当前元类的类装饰器,并创建一个从它们继承的新元类,然后在您正在装饰的类上设置这个新元类。【参考方案3】:

感谢谁给了我一些答案,帮助了我:

def getClass12(): 
     class myMultiMeta(SuperClass1.__metaclass__,SuperClass2.__metaclass__):
         pass
     return myMultiMeta
 class SuperClass12(SuperClass1,SuperClass2):
     __metaclass__ = getClass12()

是一个不错的解决方案!

【讨论】:

【参考方案4】:

正如 Michael Merickel 所指出的,getClass() 函数需要在每次调用时返回相同的对象才能使您的继承工作。仅仅相同的类是不够的。这是一种方法,滥用一个类使其表现得像你的getClass() 函数。由于类定义只执行一次,MyMetaClass1 返回时始终是同一个对象。

class getClass1(object):
    class MyMetaClass1(type):
        def __new__(cls, name, bases, dct):
            print dct.get("Attr","")+"Meta1"
            return super(MyMetaClass1, cls).__new__(cls, name, bases, dct)
    class __metaclass__(type):
        def __call__(cls):
            return cls.MyMetaClass1

你也可以这样做,使用一个真实的函数并滥用一个可变的默认参数来缓存类实例:

def getClass1(cls=[]):
    if not cls:
        class MyMetaClass1(type):
            def __new__(cls, name, bases, dct):
                print dct.get("Attr","")+"Meta1"
                return super(MyMetaClass1, cls).__new__(cls, name, bases, dct)
        cls.append(MyMetaClass1)
    return cls[0]

您会注意到这里反复出现的主题:滥用 Python 功能。 :-) 这通常表明您正在做一些可能以其他方式做得更好的事情。例如,您为什么要使用工厂,而不是仅仅以正常方式定义类?我见过类工厂有一些用处,但我认为我从未在野外见过元类工厂。

【讨论】:

以上是关于来自元类工厂的多重继承的主要内容,如果未能解决你的问题,请参考以下文章

unittest 模拟和多重继承:TypeError:元类冲突

涉及 Enum 的多重继承元类冲突

元类冲突、多重继承、实例为父

Django:从带有元的抽象类的多重继承

来自两个派生类的多重继承

来自两个抽象类的多重继承 (Qt)