Python circular import?

So i get this error

Traceback (most recent call last): File "/Users/alex/dev/runswift/utils/sim2014/simulator.py", line 3, in <module> from world import World File "/Users/alex/dev/runswift/utils/sim2014/world.py", line 2, in <module> from entities.field import Field File "/Users/alex/dev/runswift/utils/sim2014/entities/field.py", line 2, in <module> from entities.goal import Goal File "/Users/alex/dev/runswift/utils/sim2014/entities/goal.py", line 2, in <module> from entities.post import Post File "/Users/alex/dev/runswift/utils/sim2014/entities/post.py", line 4, in <module> from physics import PostBody File "/Users/alex/dev/runswift/utils/sim2014/physics.py", line 21, in <module> from entities.post import Post ImportError: cannot import name Post 

and you can see that i use the same import statement further and it works? Is there some unwritten rule about cyclic import? How can I use the same class further up the call stack?

+86
python import circular-dependency
Mar 05 '14 at 2:26
source share
6 answers

I think the answer from jpmc26, although not erroneous, is affecting circular imports too much. They can work just fine if you configure them correctly.

The easiest way to do this is to use the import my_module syntax, not from my_module import some_object . The first will almost always work, even if my_module enabled, it imports us back. The latter only works if my_object already defined in my_module , which may not be true during cyclic import.

To be specific in your case: try changing entities/post.py to import physics and then refer to physics.PostBody and not directly to PostBody . Similarly, modify physics.py to execute import entities.post and then use entities.post.Post and not just Post .

+129
Mar 05 '14 at 22:30
source share

When you import a module (or its member) for the first time, the code inside the module is executed sequentially, like any other code; for example, it is not processed otherwise than the body of the function. import is just a command like any other (assignment, function call, def , class ). Assuming your import is happening at the top of the script, here's what happens:

  • When you try to import World from World , the World script is executed.
  • World script imports Field , which causes the entities.field script to execute.
  • This process continues until you reach the entities.post script because you tried to import Post
  • entities.post script invokes physics module execution as it tries to import PostBody
  • Finally, physics tries to import Post from entities.post
  • I'm not sure if there is still an entities.post module in memory, but that really doesn't matter. Either the module is not in memory, or the module does not yet have a Post member, because it has not yet completed execution to determine Post
  • In any case, an error occurs because Post does not need to be imported

So no, it doesn’t "work further in the call stack." This is a trace of the stack where the error occurred, which means that she mistakenly tried to import Post into this class. You should not use circular import. In the best case, it has little benefit (usually no benefit), and this causes such problems. This is the burden of any developer supporting him, forcing them to walk on eggshells so as not to break it. Update your module organization.

+48
Mar 05 '14 at 2:34
source share

To understand circular dependencies, you need to remember that Python is essentially a scripting language. Execution of statements outside methods occurs at compile time. Import statements are executed just like method calls, and you must think of them as method calls to understand them.

When you import, what happens depends on whether the file you are importing exists in the module table. If so, Python uses what is currently in the character table. If not, Python starts reading the module file, compiling / executing / importing whatever it finds. The characters referenced at compile time are found or not, depending on whether they were viewed or not yet seen by the compiler.

Imagine you have two source files:

X.py file

 def X1: return "x1" from Y import Y2 def X2: return "x2" 

Y.py file

 def Y1: return "y1" from X import X1 def Y2: return "y2" 

Now suppose you compile an X.py file. The compiler starts by defining the X1 method, and then removes the import statement in X.py. This causes the compiler to pause X.py compilation and start compiling Y.py. Soon after, the compiler enters the import statement in Y.py. Since X.py is already in the module table, Python uses the existing incomplete X.py character table to satisfy any requested links. Any characters that appear before the import statement in X.py are now in the character table, but there are no characters after that. Since X1 now appears before the import statement, it is successfully imported. Python then resumes compiling Y.py. In doing so, it defines Y2 and completes the compilation of Y.py. He then resumes compiling X.py and finds Y2 in the Y.py character table. Compilation ultimately ends without error.

Something completely different happens if you try to compile Y.py from the command line. When compiling Y.py, the compiler enters the import statement before it defines Y2. He then begins compiling X.py. Soon, he gets into the import statement in X.py, which requires Y2. But Y2 is undefined, so compilation fails.

Note that if you change X.py to import Y1, compilation will always succeed, no matter which file you compile. However, if you modify the Y.py file to import the X2 character, no file will be compiled.

At any time when module X or any module imported by X can import the current module, DO NOT use:

 from X import Y 

Every time you think that there may be circular imports, you should also avoid referencing variables in other modules. Consider the innocent looking code:

 import X z = XY 

Suppose a module X imports this module before this module imports X. Next, suppose that Y is defined in X after the import statement. Then Y will not be determined when this module is imported, and you will get a compilation error. If this module first imports Y, you can handle it. But when one of your employees innocently redefines the definitions in the third module, the code will break.

In some cases, you can resolve circular dependencies by moving the import statement below the characters needed for other modules. In the above examples, definitions are never broken before import. Definitions after the import statement sometimes fail, depending on the compilation order. You can even put import instructions at the end of the file if none of the imported characters are required at compile time.

Note that moving import statements in a module hides what you are doing. Compensate for this with a comment at the top of your module like this:

 #import X (actual import moved down to avoid circular dependency) 

In general, this is bad practice, but sometimes it is difficult to avoid.

+32
Oct 17 '16 at 19:45
source share

For those of you who, like me, came up with this question from Django, you should know that the docs provide a solution: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey

"... To refer to models defined in another application, you can explicitly specify the model with the full label of the application. For example, if the manufacturer’s model is defined above in another application named production, you need to use:

 class Car(models.Model): manufacturer = models.ForeignKey( 'production.Manufacturer', on_delete=models.CASCADE, ) 

This link may be useful when resolving circular import dependencies between two applications. ... "

+14
Sep 26 '16 at 20:46
source share

If you run into this problem in a rather complicated application, it can be cumbersome to reorganize your entire import. PyCharm offers a quick fix for this, which will automatically change all use of imported characters.

enter image description here

+2
Sep 14 '18 at 10:39
source share

I used the following:

 from module import Foo foo_instance = Foo() 

but to get rid of circular reference I did the following and it worked:

 import module.foo foo_instance = foo.Foo() 
0
Jul 22 '19 at 8:52
source share



All Articles