Python: circular imports needed for type checking

First of all: I know that there are many questions and answers on the topic of circular imports.

The answer is more or less: "Correctly create the module / class structure, and you will not need cyclic import." It's true. I tried very hard to make the right project for my current project, I, in my opinion, was successful with this.

But my specific problem is this: I need type checking in a module that is already imported by the module containing the class to check. But this leads to an import error.

Same:

foo.py:

from bar import Bar class Foo(object): def __init__(self): self.__bar = Bar(self) 

bar.py:

 from foo import Foo class Bar(object): def __init__(self, arg_instance_of_foo): if not isinstance(arg_instance_of_foo, Foo): raise TypeError() 

Solution 1: If I changed it to check the type by comparing strings, it will work. But I don't like this solution (string comparison is quite expensive for simple type checking and there may be a problem when it comes to refactoring).

bar_modified.py:

 from foo import Foo class Bar(object): def __init__(self, arg_instance_of_foo): if not arg_instance_of_foo.__class__.__name__ == "Foo": raise TypeError() 

Solution 2: I could also pack two classes in one module. But in my project there are many different classes, such as "Bar", and I want to split them into different module files.

After my own 2 solutions are not suitable for me: does anyone have a better solution to this problem?

+8
python import circular-dependency
source share
4 answers

The best solution is not type checking.

Another solution is to not create an instance, not a link at all, Foo or Bar , until both classes are loaded. If the first module is loaded first, do not create a Bar or refer to Bar until the class Foo statement is executed. Similarly, if the second module is loaded first, do not create Foo or reference Foo until the class Bar statement is executed.

This is basically the source of ImportError , which could have been avoided if you instead imported "foo" and "import bar" and used foo.Foo , where you now use Foo and bar.Bar , where you now use Bar . In this case, you no longer refer to any of them until Foo or Bar is created, which, I hope, will not happen until both are created (or you get an AttributeError ).

+5
source share

You can program on an interface (ABC is an abstract base class in python), and not a specific Bar type. This is a classic way to solve package / module interdependencies in many languages. Conceptually, this should also lead to a better design of the object model.

In your case, you define the IBar interface in some other module (or even in the module that contains the Foo class), depends on the use of this abc ). Then you indicate the following:

foo.py:

 from bar import Bar, IFoo class Foo(IFoo): def __init__(self): self.__bar = Bar(self) # todo: remove this, just sample code f = Foo() b = Bar(f) print f print b x = Bar('do not fail me please') # this fails 

bar.py:

 from abc import ABCMeta class IFoo: __metaclass__ = ABCMeta class Bar(object): def __init__(self, arg_instance_of_foo): if not isinstance(arg_instance_of_foo, IFoo): raise TypeError() 
+5
source share

You can simply defer import to bar.py as follows:

 class Bar(object): def __init__(self, arg_instance_of_foo): from foo import Foo if not isinstance(arg_instance_of_foo, Foo): raise TypeError() 
+2
source share

Possible duplicate: Python type tooltip without loop import

You should use the direct link (PEP 484 - Tooltip Type):

When a type hint contains names that are not yet defined, this definition can be expressed as a string literal, which will be resolved later.

So instead of:

 class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = right 

do:

 class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = right 
0
source share

All Articles