我的目录结构类似于以下内容:
.
├── 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
。
答案 0 :(得分:0)
我意识到这还为时已晚,但以防万一,这对偶然发现此问题的其他人有帮助...
您在这里遇到了一些问题:
您的类在register
调用中是错误的方法;在这种情况下,只有调用Base.register(ModelA)
(而不是相反)才能将ModelA
注册为Base
的“ virtual subclass”。
调用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__()
却没有-讨厌。)
如果在执行的 some 点处不导入包含ModelA
的模型,则永远不会进行设置,因此ModelA
不会显示在Base.__subclassess__()
反正。这可能就是main.py
中的字典为空的原因。
一种解决方法是在main.py
上添加一行import models
,并models/__init__.py
导入model_a
和model_b
。然后,当main
运行时,它导入models
,而后者又导入model_a
和model_a
,执行ModelA
和ModelB
的定义并添加将它们添加到Base
的类层次结构中。
在最后一行,您没有实例化klass
所指向的任何类的实例;该行应为:
klass().run()