三重inheritance导致元类冲突…有时

看起来我偶然发现了一个元类的地狱,即使我不想和它有任何关系。

我正在使用PySide在Qt4中编写一个应用程序。 我想将事件驱动的部分从Qt Designer文件生成的UI定义中分离出来。 因此,我创build了一个“控制器”类,但为了缓解我的生活,我多次inheritance它们。 一个例子:

class BaseController(QObject): def setupEvents(self, parent): self.window = parent class MainController(BaseController): pass class MainWindow(QMainWindow, Ui_MainWindow, MainController): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.setupEvents(self) 

这按预期工作。 它也有从( QDialogUi_DialogBaseController )inheritance。 但是,当我的子类BaseController并尝试从所述的子类(代替BaseController )inheritance,我收到一个错误:

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

说明: QMainWindowQDialog都从QObjectinheritance。 由于Qt事件系统的特性, BaseController也必须inheritance它。 Ui_类只能从简单的Python对象类inheritance。 我寻找解决scheme,但都涉及故意使用元类的情况。 所以我一定在做一些非常错误的事情。

编辑:我的描述可能会更清楚通过添加graphics。

工作示例:

 QObject | \___________________ | object | QMainWindow | BaseController | /---Ui_MainWindow | | | MainController MainWindow-----------------/ 

另一个工作例子:

 QObject | \___________________ | object | QDialog | BaseController | /---Ui_OtherWindow | | | | OtherWindow----------------/ 

不工作的例子:

 QObject | \___________________ | object | QDialog | BaseController | /---Ui_OtherWindow | | | OtherController OtherWindow----------------/ 

该错误消息表明您的层次结构中有两个相互冲突的元类。 你需要检查你的每个类和QT类来找出冲突的位置。

以下是一些简单的示例代码,可以设置相同的情况:

 class MetaA(type): pass class MetaB(type): pass class A: __metaclass__ = MetaA class B: __metaclass__ = MetaB 

我们不能直接inheritance这两个类,因为python不知道使用哪个元类:

 >>> class Broken(A, B): pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases 

这个错误试图告诉我们的是,我们需要通过引入第三个元类来解决这两个元类之间的冲突,这个元类是基类中所有元类的子类。

我不确定这比错误信息本身更清楚,但基本上,你通过这样做来解决它:

 class MetaAB(MetaA, MetaB): pass class Fixed(A, B): __metaclass__ = MetaAB 

此代码现在编译并正确运行。 当然,在真实的情况下,解决冲突的元类将不得不决定采用哪一种父元类行为,您需要从应用程序的需求中自行找出这些行为。

请记住,您的inheritance类只能获得两个元类之一。 __init__方法,有时做所有的工作,所以在很多情况下,你将不得不添加一个__init__调用两个以帮助他们相处。

我们使用这样的东西:

 class CooperativeMeta(type): def __new__(cls, name, bases, members): #collect up the metaclasses metas = [type(base) for base in bases] # prune repeated or conflicting entries metas = [meta for index, meta in enumerate(metas) if not [later for later in metas[index+1:] if issubclass(later, meta)]] # whip up the actual combined meta class derive off all of these meta = type(name, tuple(metas), dict(combined_metas = metas)) # make the actual object return meta(name, bases, members) def __init__(self, name, bases, members): for meta in self.combined_metas: meta.__init__(self, name, bases, members) 

假设有良好的,现代的元类实现卫生(元类的子type ,以及在__init__可以做的任何事情都可以在那里完成),这就允许许多元类相处。

__new__ ,大部分工作都需要完成的__new__ ,无论如何都很难结合。 你可以通过确保它的类是多重inheritance中的第一个元素来在这里偷偷摸摸其中的一个。

要使用这个,你只需声明:

 __metaclass__ = CooperativeMeta 

对于那些不同的元类聚集在一起的类。

在这种情况下,例如:

 class A: __metaclass__ = MetaA class B: __metaclass__ = MetaB class Fixed(A, B): __metaclass__ = CooperativeMeta 

对于不同的MetaA和MetaB而言,这种方法可以正确地工作,而不仅仅是将它们一起inheritance以closures编译器。

希望评论解释代码。 只有一个棘手的问题,就是要去除从不同地方inheritance的任何给定__metaclass__多余调用,并允许没有显式元类的类与其他__metaclass____metaclass__ 。 如果它看起来过度,你可以省略它,并在你的代码中,仔细地订购基类。

这使得解决scheme的三条路线非常清晰。