python-3.x - 获取 ABCMeta 的所有已注册子类
问题描述
我有一个类似于以下的目录结构:
.
├── main.py
├── model.py
└── models
├── __init__.py
├── model_a.py
└── model_b.py
model.py
包含一个抽象基类:
from abc import ABCMeta, abstractmethod
class Base(metaclass=ABCMeta):
@abstractmethod
def run(self):
pass
文件夹中models
是这个基类的两个实现,model_a.py
和model_b.py
,它们将自己注册到主Base
类。 model_a.py
看起来像这样:
from model import Base
class ModelA(Base):
def run(self):
return "a"
ModelA.register(Base)
assert issubclass(ModelA, Base)
并且model_b.py
是相似的。
现在,我要做的main.py
是创建一个包含所有子类的字典,Base
以便我可以选择一个(通过我的程序的 GUI)并运行它:
from model import Base
subclasses = Base.__subclasses__()
dct = {cls.__name__: cls for cls in subclasses}
klass = dct['ModelA']
klass.run()
但我无法让它工作。RuntimeError: Refusing to create an inheritance cycle
当我尝试执行派生类之一并且字典main.py
为空时,我得到了。
解决方案
我意识到这已经很晚了,但如果它对其他偶然发现这个的人有帮助......
您在这里遇到了一些问题:
你的课程在那个
register
电话中是错误的;只有在这种情况下调用Base.register(ModelA)
(而不是反过来)才能注册ModelA
为.Base
调用
ModelA.register(Base)
正在尝试注册Base
为 的虚拟子类ModelA
,但ModelA
已经是 , 的实际子类Base
- 这就是您获得继承周期的原因。您不能让类 X 和 Y 相互继承。但是,作为
ModelA
明确的子类Base
,您根本不需要调用register
。你想要:class ModelA(Base): ...
没有
register
调用(这里ModelA
是 的实际子类Base
),或者:class ModelA: ... Base.register(ModelA)
(这里
ModelA
是一个独立的类,在Base
的继承层次之外,但它被注册为一个虚拟子类)。要么/或 - 不是两者兼而有之。在任何一种情况下,
issubclass(ModelA, Base)
都会是True
。__subclasses__()
不选择虚拟子类,只有实际子类 - 所以如果你想使用它,你应该忘记register()
并只创建ModelA
一个真正的子类Base
(上面的第一个选项)。(在我看来,这是整个 ABC/
register
机制的一个缺陷:issubclass()
可能是True
但__subclasses__()
不会接受 - 讨厌。)ModelA
如果您在执行的某个时刻不导入包含的模型,则它永远不会设置,因此无论如何ModelA
都不会出现。Base.__subclassess__()
这可能是字典main.py
为空的原因。一个解决方法是在
main.py
说添加一行import models
,并models/__init__.py
导入model_a
和model_b
。然后在main
运行时导入models
,然后导入model_a
andmodel_a
,执行 and 的定义ModelA
并将ModelB
它们添加到Base
的类层次结构中。在你的最后一行,你没有实例化任何类
klass
所指向的实例;该行应该是:klass().run()