首页 > 解决方案 > 获取 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.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为空时,我得到了。

标签: python-3.xsubclassabstract-base-class

解决方案


我意识到这已经很晚了,但如果它对其他偶然发现这个的人有帮助......

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

  1. 你的课程在那个register电话中是错误的;只有在这种情况下调用Base.register(ModelA)(而不是反过来)才能注册ModelA为.Base

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

  3. 但是,作为ModelA明确的子类Base,您根本不需要调用register。你想要:

    class ModelA(Base):
       ...
    

    没有register调用(这里ModelA是 的实际子类Base),或者:

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

    (这里ModelA是一个独立的类,在Base的继承层次之外,但它被注册为一个虚拟子类)。要么/或 - 不是两者兼而有之。

    在任何一种情况下,issubclass(ModelA, Base)都会是True

  4. __subclasses__()不选择虚拟子类,只有实际子类 - 所以如果你想使用它,你应该忘记register()并只创建ModelA一个真正的子类Base(上面的第一个选项)。

    (在我看来,这是整个 ABC/register机制的一个缺陷:issubclass()可能是True__subclasses__()不会接受 - 讨厌。)

  5. ModelA如果您在执行的某个时刻不导入包含的模型,则它永远不会设置,因此无论如何ModelA都不会出现。Base.__subclassess__()这可能是字典main.py为空的原因。

    一个解决方法是在main.py说添加一行import models,并models/__init__.py导入model_amodel_b。然后在main运行时导入models,然后导入model_aand model_a,执行 and 的定义ModelA并将ModelB它们添加到Base的类层次结构中。

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

    klass().run()
    

推荐阅读