获取ABCMeta的所有注册子类

时间:2018-07-12 13:04:11

标签: python-3.x subclass abstract-base-class

我的目录结构类似于以下内容:

.
├── 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.pymodel_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

1 个答案:

答案 0 :(得分:0)

我意识到这还为时已晚,但以防万一,这对偶然发现此问题的其他人有帮助...

您在这里遇到了一些问题:

  1. 您的类在register调用中是错误的方法;在这种情况下,只有调用Base.register(ModelA)(而不是相反)才能将ModelA注册为Base的“ virtual subclass”。

  2. 调用ModelA.register(Base)试图将Base注册为ModelA的虚拟子类,但是ModelA已经是Base的实际子类, -这就是为什么获得继承周期的原因。 X和Y不能相互继承。

  3. 但是,由于ModelA显然是Base的子类,因此您根本不需要调用register。您想要:

    class ModelA(Base):
       ...
    

    不进行register调用(此处ModelABase的实际子类),或者:

    class ModelA:
        ...
    
    Base.register(ModelA)
    

    (这里ModelA是一个独立的类,位于Base的继承层次结构之外,但已注册为虚拟子类)。要么-要么都不是。

    无论哪种情况,issubclass(ModelA, Base)都将是True

  4. __subclasses__()不会选择虚拟子类,而只会选择实际的子类-因此,如果您要使用它,则应该忘掉register(),而只需将ModelA变成真正的子类即可。 Base的子类(上面的第一个选项)。

    (在我看来,这是具有整个ABC / register机制的疣:issubclass()可能是True,但__subclasses__()却没有-讨厌。)

  5. 如果在执行的 some 点处不导入包含ModelA的模型,则永远不会进行设置,因此ModelA不会显示在Base.__subclassess__()反正。这可能就是main.py中的字典为空的原因。

    一种解决方法是在main.py上添加一行import models,并models/__init__.py导入model_amodel_b。然后,当main运行时,它导入models,而后者又导入model_amodel_a,执行ModelAModelB的定义并添加将它们添加到Base的类层次结构中。

  6. 在最后一行,您没有实例化klass所指向的任何类的实例;该行应为:

    klass().run()