I recently ran into a circular import problem in Python. In my search for an answer, I often came across people simply stating that "circular imports are OK." with little to no explanation. Don't be deceived! There are cases where circular imports do and don't work! This gist will hopefully illustrate those cases.
Edit: A good Discussion about cyclic imports. Also check this Stack Overflow Thread. The answer by pythoneer (at the time of writing, the second most voted answer), is one of the misleading statements that drove me to write this gist.
Say we have two classes, Lefty and Righty, each in their own file.
# File Righty.py
import Lefty
class Righty:
def __init__(self):
print("Righty")
def get_a_lefty(self):
return Lefty.Lefty()
# File Lefty.py
import Righty
class Lefty:
def __init__(self):
print("Lefty")
def get_a_righty(self):
return Righty.Righty()
and a third file to run something
# File Main.py
import Righty
myRighty = Righty.Righty()
myLefty = myRighty.get_a_lefty()
When Main.py is run, the output is Righty Lefty
Circular imports work!
Say we have two classes, Tank and BlueTank, each in their own file. (Beware: This is an extremely contrived example for illustration)
# File Tank.py
import BlueTank
class Tank:
def __init__(self):
print("Tank")
def get_blue_tank(self):
return BlueTank.BlueTank()
# File BlueTank.py
import Tank
# Notice this line
class BlueTank(Tank.Tank):
def __init__(self):
print("BlueTank")
and again, a third file to run it,
# File Main.py
import Tank
myTank = Tank.Tank()
myBlueTank = myTank.get_blue_tank()
When Main.py is run, the output is
Traceback (most recent call last):
File "./Main.py", line 8, in <module>
import Tank
File ".\Tank.py", line 1, in <module>
import BlueTank
File ".\BlueTank.py", line 4, in <module>
class BlueTank(Tank.Tank):
AttributeError: module 'Tank' has no attribute 'Tank'
Uh oh! It doesn't work! The line that catches my attention is
AttributeError: module 'Tank' has no attribute 'Tank'
That's clearly not true. Tank is a class in the Tank.py file... what's happening?
To understand the difference, take a look at this example
# File Main.py (only contains an import statement)
import MyClass
# File MyClass.py (only contains a print statement)
print("Cat")
When Main.py is run, the output is
Cat
When "import MyClass" is executed, it executes each statement in MyClass.py. When python imports a module, it executes each statement in it. Put simply, it executes each statement with no indent before it. Statements that are often like this are import <module>
, class <classname>:
, def <function>:
, or, like in the example, function calls like print("Cat").
In order for the interpreter to execute these statements, it must know what the words in the statement are! Going back to the Tank example,
class BlueTank(Tank.Tank):
notice that the class
statement uses the name Tank.Tank.
Thus, in order for the interpreter to execute this statement, Tank.Tank must already be defined. We have a Tank class in Tank.py so why doesn't it know Tank.Tank? Take another look at the error trace
Traceback (most recent call last):
File "./Main.py", line 8, in <module>
import Tank
File ".\Tank.py", line 1, in <module>
import BlueTank
File ".\BlueTank.py", line 4, in <module>
class BlueTank(Tank.Tank):
AttributeError: module 'Tank' has no attribute 'Tank'
Notice the first error statement in the trace! It's import Tank
. That means the interpreter was still in the process of importing Tank when we tried to access Tank.Tank
. The interpreter reached class BlueTank(Tank.Tank):
before it reached the class Tank
line. To illustrate this a little bit, we can tell the interpreter to import BlueTank
after the class Tank:
statement.
# File Tank.py
class Tank:
def __init__(self):
print("Tank")
def get_blue_tank(self):
return BlueTank.BlueTank()
import BlueTank
It works! The output is
Tank
Tank
BlueTank
Dr. Seuss would almost be proud. So, as shown in the When It Works example, given modules A and B that import each other, circular import works when B does not have to know about anything in A (classes, functions, etc) while A is importing B. That sure is a mouthful, and probably not at all clear, but the examples above should help understanding.
But the When It Doesn't Work example illustrates something else. Notice that you can call BlueTank.BlueTank()
in the Tank
class before the import BlueTank
statement. While in the Tank.py
file, BlueTank.BlueTank()
comes before import BlueTank,
the interpreter reaches the import BlueTank
statement before it reaches the BlueTank.BlueTank()
statement. Thus, in that case, circular import works. So the bold statement above is true, but there are more cases where it works.
But gosh darn that fix is ugly. If you find yourself having to do this, you may want to rethink your design (class hierarchy, function ordering, etc.) instead of putting import
statements at various locations in your files. You generally want those at the top.