获取 ABCMeta 的所有注册子类

Getting all registered subclasses of an ABCMeta

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

.
├── main.py
├── model.py
└── models
    ├── __init__.py
    ├── model_a.py
    └── model_b.py

model.py 包含一个抽象基础 Class:

from abc import ABCMeta, abstractmethod

class Base(metaclass=ABCMeta):

    @abstractmethod
    def run(self):
        pass

models 文件夹中是此基础 class、model_a.pymodel_b.py 的两个实现,它们将自己注册到主 Baseclass。 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 的所有子 class 的字典,这样我就可以 select (通过我程序的 GUI)和 运行 它:

from model import Base

subclasses = Base.__subclasses__()

dct = {cls.__name__: cls for cls in subclasses}

klass = dct['ModelA']
klass.run()

但我无法让它工作。当我尝试执行派生的 classes 之一并且 main.py 中的字典为空时,我得到 RuntimeError: Refusing to create an inheritance cycle

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

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

  1. 你的 classes 在那个 register 调用中是错误的;只有在这种情况下调用 Base.register(ModelA)(而不是相反)才有意义,以便将 ModelA 注册为 Base.[=58 的“virtual subclass” =]

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

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

    class ModelA(Base):
       ...
    

    没有 register 调用(此处 ModelABase 的实际子 class),或者:

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

    (这里ModelA是一个独立的class,在Base的继承层次之外,但它被注册为一个虚拟子class)。 Either/or - 两者都不是。

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

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

    (在我看来,这是整个 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_amodel_a,执行 ModelAModelB 的定义并添加他们到 Base 的 class 层次结构。

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

    klass().run()