获取 ABCMeta 的所有已注册子类

Bio*_*eek 3 subclass abstract-base-class python-3.x

我有一个类似于以下的目录结构:

\n\n
.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 model.py\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 models\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 model_a.py\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 model_b.py\n
Run Code Online (Sandbox Code Playgroud)\n\n

model.py包含一个抽象基类:

\n\n
from abc import ABCMeta, abstractmethod\n\nclass Base(metaclass=ABCMeta):\n\n    @abstractmethod\n    def run(self):\n        pass\n
Run Code Online (Sandbox Code Playgroud)\n\n

models文件夹中有该基类的两个实现,model_a.pymodel_b.py,它们将自己注册到主Base类。 model_a.py看起来像这样:

\n\n
from model import Base\n\nclass ModelA(Base):\n\n    def run(self):\n        return "a"\n\nModelA.register(Base)\n\nassert issubclass(ModelA, Base)\n
Run Code Online (Sandbox Code Playgroud)\n\n

并且model_b.py是相似的。

\n\n

现在,我想做的main.py是创建一个包含所有子类的字典Base,以便我可以选择一个子类(通过程序的 GUI)并运行它:

\n\n
from model import Base\n\nsubclasses = Base.__subclasses__()\n\ndct = {cls.__name__: cls for cls in subclasses}\n\nklass = dct[\'ModelA\']\nklass.run()\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n

gim*_*and 5

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

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

  1. 你的课程在那个电话中是错误的registerBase.register(ModelA)只有在这种情况下,调用(而不是相反)才能注册ModelA为 的“虚拟子类”才有意义Base

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

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

    class ModelA(Base):
       ...
    
    Run Code Online (Sandbox Code Playgroud)

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

    class ModelA:
        ...
    
    Base.register(ModelA)
    
    Run Code Online (Sandbox Code Playgroud)

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

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

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

    (在我看来,这是一个带有整个 ABC/register机制的疣:issubclass()可能是True,但__subclasses__()没有发现它 - 令人讨厌。)

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

    解决方法是在, 和 has import andmain.py中添加一行。然后,在运行时,它导入,进而导入和,执行和的定义并将它们添加到的类层次结构中。import modelsmodels/__init__.pymodel_amodel_bmainmodelsmodel_amodel_aModelAModelBBase

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

    klass().run()
    
    Run Code Online (Sandbox Code Playgroud)