Summary
TLDR: I have an ABC with severel subclasses. The ABC has a method that returns a subclass instance. I want to put the ABC and the subclasses in distinct files.
Example
In one file, this works:
from abc import ABC, abstractmethod
class Animal(ABC):
# Methods to be implemented by subclass.
@property
@abstractmethod
def name(self) -> str:
"""Name of the animal."""
...
@abstractmethod
def action(self):
"""Do the typical animal action."""
...
# Methods directly implemented by base class.
def turn_into_cat(self):
return Cat(self.name)
class Cat(Animal):
def __init__(self, name):
self._name = name
name = property(lambda self: self._name)
action = lambda self: print(f"{self.name} says 'miauw'")
class Dog(Animal):
def __init__(self, name):
self._name = name
name = property(lambda self: self._name)
action = lambda self: print(f"{self.name} says 'woof'")
>>> mrchompers = Dog("Mr. Chompers")
>>> mrchompers.action()
Mr. Chompers says 'woof'
>>> mrchompers.turn_into_cat().action()
Mr. Chompers says 'miauw'
Issue
I want to put the Animal class definition in base.py, and the Cat and Dog class definitions in subs.py.
The problem is, that this leads to cyclic imports. base.py must include a from .subs import Cat, and subs.py must include a from .base import Animal.
I've incountered cyclic import errors before, but usually when type hinting. In that case I can put the lines
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .base import Animal
However, that is not the case here.
Any ideas as to how to split this code up into 2 files?
I'm not sure making
Animaldepend on a subclass defined in another file is a great idea in the first place, but since the actual value ofCatisn't needed untilturn_into_catis actually called, one hack would be to givebase.Cata dummy value thatsubspatches onceCatis defined.Note that
baseno longer needs to know anything aboutsubs, butAnimalwon't be fully ready to use untilsubs.pyis executedAs soon as
Catis defined, the namebase._Catis updated to the class whichAnimal.turn_into_catneeds in order to create its return value.