I have a directory structure similar to the following:
.
├── main.py
├── model.py
└── models
├── __init__.py
├── model_a.py
└── model_b.py
model.py
contains an Abstract Base Class:
from abc import ABCMeta, abstractmethod
class Base(metaclass=ABCMeta):
@abstractmethod
def run(self):
pass
in the models
folder are two implementations of this base class, model_a.py
and model_b.py
, who register themselves to the main Base
class. model_a.py
looks like this:
from model import Base
class ModelA(Base):
def run(self):
return "a"
ModelA.register(Base)
assert issubclass(ModelA, Base)
and model_b.py
is similar.
Now, what I am trying to do in main.py
is to create a dictionary of all the subclasses of Base
so that I can select one (via the GUI of my program) and run it:
from model import Base
subclasses = Base.__subclasses__()
dct = {cls.__name__: cls for cls in subclasses}
klass = dct['ModelA']
klass.run()
But I can't get it to work. I get RuntimeError: Refusing to create an inheritance cycle
when I try to execute one of the derived classes and the dictionary in main.py
is empty.
I realise this is rather late, but in case it's helpful to anyone else who stumbles upon this...
You've got a few problems here:
Your classes are the wrong way round in that register
call; it would only make sense in this context to call Base.register(ModelA)
(not the other way round) in order to register ModelA
as a "virtual subclass" of Base
.
Calling ModelA.register(Base)
is trying to register Base
as a virtual subclass of ModelA
, but ModelA
is already an actual subclass of Base
, - which is why you're getting an inheritance cycle. You can't have classes X and Y inheriting from each other.
However, as ModelA
is explicitly a subclass of Base
, you don't need to call register
at all. You want either:
class ModelA(Base):
...
with no register
call (here ModelA
is an actual subclass of Base
), or:
class ModelA:
...
Base.register(ModelA)
(here ModelA
is a standalone class, outside Base
's inheritance hierarchy, but it is registered as a virtual subclass). Either/or - not both.
In either case, issubclass(ModelA, Base)
would be True
.
__subclasses__()
doesn't pick up virtual subclasses, only actual ones - so if you want to use that, you should forget about register()
and just make ModelA
a real subclass of Base
(the first option above).
(This is, to my mind, a wart with the whole ABC/register
mechanism: issubclass()
might be True
but __subclasses__()
doesn't pick it up - nasty.)
If you don't import the model containing ModelA
at some point in your execution, it's never set up so ModelA
won't show up in Base.__subclassess__()
anyway. This is probably why the dictionary in main.py
is empty.
A fix would be to add a line to main.py
saying import models
, and have models/__init__.py
import model_a
and model_b
. Then when main
runs, it imports models
, which in turn imports model_a
and model_a
, executing the definitions of ModelA
and ModelB
and adding them to Base
's class hierarchy.
On your final line, you don't instantiate an instance of whatever class klass
is pointing at; the line should be:
klass().run()