Search code examples
pythonpython-3.xcircular-dependency

Checking the child / parent relationship of a class while preventing circular import / circular dependency


I have these modules:

# module 1

from abc import ABC
from module3 import AnotherClass


class Parent(ABC):

    @classmethod
    def func1(cls):

        other_param = "foo"
        AnotherClass.some_func(cls, other_param)
# module 2

from module1 import Parent
from module3 import AnotherClass


class Child1(Parent):

    pass


class Child2(Parent):

    pass
# module 3

from module1 import Parent # only for checking if the object is a subclass of Parent


class AnotherClass(object):

    @classmethod
    def some_func(cls, child_cls, other_param):

        assert issubclass(child_cls, Parent)
        # do other stuff

now if I do this:

c1 = Child1()

c1.func1()

I rightfully get an ImportError complaining from a circular import as follows:

ImportError: cannot import name 'Parent' from partially initialized module 'module1' (most likely due to a circular import)

Actually AnotherClass does not depend on Parent or any of the Child classes, I'm importing the Parent only for checking if the passed class is a subclass of Parent, in this row:

assert issubclass(child_cls, Parent)

Now, I can defer importing of the Parent like this:

# module 3


class AnotherClass(object):

    @classmethod
    def some_func(cls, child_cls, other_param):

        from module1 import Parent # only for type checking
        assert issubclass(child_cls, Parent)
        # do other stuff

And it works like a charm. But for some reason (unknown to me) I feel uncomfortable doing it this way.

Now my question is, can I sleep with peace deferring the import like that, or would it bite me in some way later on down the road?

If yes, how would you suggest I do the this parent / child relationship check at AnotherClass.some_func()?

P.S. Before anyone brings it up, I know this topic of circular dependencies has been asked and answered many many times (such as this one), but I'm asking if this specific solution is feasible and problem-free in the long run.


Solution

  • There's nothing inherently wrong with your current solution. In this case you could also solve it like this though, which is imo a bit cleaner:

    import module1
    
    
    class AnotherClass(object):
    
        @classmethod
        def some_func(cls, child_cls, other_param):
            assert issubclass(child_cls, module1.Parent)