In Python, a circular import is when two files each try to import the other, causing a failure when a module isn’t fully initialized. The best way to fix this situation is to organize your code in layers so that the importing relationships naturally flow in just one direction. But sometimes it works to simply change the style of import statement you use. I’ll show you.
Let’s say you have these files:
1# one.py
2from two import func_two
3
4def func_one():
5 func_two()
1# two.py
2from one import func_one
3
4def do_work():
5 func_one()
6
7def func_two():
8 print("Hello, world!")
1# main.py
2from two import do_work
3do_work()
If we run main.py, we get this:
% python main.py
Traceback (most recent call last):
File "main.py", line 2, in <module>
from two import do_work
File "two.py", line 2, in <module>
from one import func_one
File "one.py", line 2, in <module>
from two import func_two
ImportError: cannot import name 'func_two' from partially initialized
module 'two' (most likely due to a circular import) (two.py)
When Python imports a module, it executes the file line by line. Every global
in the file (top-level name including functions and classes) becomes an
attribute on the module object being constructed. In two.py, we import from
one.py at line 2. At that moment, the two
module has been created, but it
has no attributes yet because nothing has been defined yet. It will eventually
have do_work
and func_two
, but we haven’t executed those def
statements yet, so they don’t exist. Like a function call, when the import
statement is run, it begins executing the imported file, and doesn’t come back
to the current file until the import is done.
The import of one.py starts, and its line 2 tries to get a name from the two
module. As we just said, the two
module exists, but has no names defined
yet. That gives us the error.
Instead of importing names from modules, we can import whole modules instead. All we do is change the form of the imports, and how we reference the functions from the imported modules, like this:
1# one.py
2import two # was: from two import func_two
3
4def func_one():
5 two.func_two() # was: func_two()
1# two.py
2import one # was: from one import func_one
3
4def do_work():
5 one.func_one() # was: func_one()
6
7def func_two():
8 print("Hello, world!")
1# main.py
2from two import do_work
3do_work()
Running the fixed code, we get this:
% python main.py
Hello, world!
It works because two.py imports one
at line 2, and then one.py imports
two
at its line 2. That works just fine, because the two
module
exists. It’s still empty like it was before the fix, but now we aren’t trying
to find a name in it during the import. Once all of the imports are done, the
one
and two
modules both have all their names defined, and we can
access them from inside our functions.
The key idea here is that “from two import func_two” tries to find
func_two
during the import, before it exists. Deferring the name lookup
to the body of the function by using “import two” lets all of the modules get
themselves fully initialized before we try to use them, avoiding the circular
import error.
As I mentioned at the top, the best way to fix circular imports is to structure your code so that modules don’t have mutual dependencies like this. But that isn’t always easy, and this can buy you a little time to get your code working again.
Comments
I like it! Simple and usefully
Thanks a lot
Add a comment: