三重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)
这按预期工作。 它也有从( QDialog
, Ui_Dialog
, BaseController
)inheritance。 但是,当我的子类BaseController
并尝试从所述的子类(代替BaseController
)inheritance,我收到一个错误:
TypeError:调用元类时出错基类元类冲突:派生类的元类必须是其所有元的元类的(非严格)子类
说明: QMainWindow
和QDialog
都从QObject
inheritance。 由于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的三条路线非常清晰。