我正在 PySide6 中构建一个应用程序,它将涉及插件的动态加载。为了实现这一点,我使用
ABCMeta
为插件接口定义一个自定义元类,并且我希望这个自定义元类从 ABC
和 QObject
继承,以便我可以抽象尽可能多的行为,包括所有子类通用的标准信号和槽之类的东西。
我已经建立了一个 MWE,它显示了使我能够使此设置正常工作的逻辑链,但继承链比我想象的要更深,并且 ABC 的执行似乎并没有贯彻执行(从某种意义上说,不覆盖
@abstractmethod
不会导致错误)。是否可以在保留所需继承的同时缩短此时间?最后,我的目标是让 print_value()
类作为继承自 ABC 的抽象基类,以便我可以在其中定义 MetaTab
,然后为各个插件创建子类。我真的需要 @abstractmethod
和 QABCMeta
来完成这项工作,还是有办法清理它,消除这个继承链中的一个链接?QObjectMeta
我发现的解决方法是在元类中重新实现 ABCMeta 的一些行为,该元类结合了 QOBjects 的元类和 ABCMeta 本身 - 通过以下更改,
from abc import ABC, ABCMeta, abstractmethod
from PySide6.QtCore import QObject
class QABCMeta(ABCMeta, type(QObject)):
pass
class QObjectMeta(ABC, metaclass=QABCMeta):
pass
class MetaTab(QObject, QObjectMeta):
def __init__(self, x):
print('initialized')
self.x = x
@abstractmethod
def print_value(self):
pass
class Tab(MetaTab):
def __init__(self, x):
super().__init__(x)
def print_value(self):
print(self.x)
def main():
obj = Tab(5)
for b in obj.__class__.__bases__:
print("Base class name:", b.__name__)
print("Class name:", obj.__class__.__name__)
obj.print_value()
if __name__=='__main__':
main()
行为将恢复:
@abstractmethod
使用从“MetaTab”派生的类(未实现
class QABCMeta(ABCMeta, type(QObject)):
def __new__(mcls, name, bases, ns, **kw):
cls = super().__new__(mcls, name, bases, ns, **kw)
abc._abc_init(cls)
return cls
def __call__(cls, *args, **kw):
if cls.__abstractmethods__:
raise TypeError(f"Can't instantiate abstract class {cls.__name__} without an implementation for abstract methods {set(cls.__abstractmethods__)}")
return super().__call__(*args, **kw)
)在 REPL 上进行测试将按预期引发:
print_value
至于您构建的层次结构:您可能可以跳过一些中间类,但我要更改的是避免使用名称“Meta”来表示不是
元类(即用于构建类的类)它们本身,并且被传递给 In [112]: class T2(MetaTab):
...: pass
...:
In [113]: T2(1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[113], line 1
----> 1 T2(1)
Cell In[105], line 11, in QABCMeta.__call__(cls, *args, **kw)
(...)
Can't instantiate abstract class T2 without an implementation for abstract methods : {'print_value'})
命名参数) - QObjectMeta 和 MetaTab 都是令人困惑的名称,因此 - 您可以使用“Base”或“Mixin”之类的中缀来代替。
metaclass=
的课程可能只是:
QobjectMeta
然后你的“MetaTab”就不需要显式地从 QObject 继承。 (不需要从 ABC 显式继承并恢复元类行为):
class AQObjectBase(QObject, metaclass=QABCMeta):
pass