Resolving Circular Imports in Python by Refactoring Import Statements
This article explains the problem of circular imports in Python, demonstrates the resulting ImportError with example modules, and shows how to resolve it by changing import statements to module-level imports, allowing the code to run correctly and print 'Hello, world!'.
In Python, a circular import occurs when two modules import each other, causing an ImportError because one module is only partially initialized when the other tries to access its names.
Consider the following three files that illustrate the issue:
<code># one.py
from two import func_two
def func_one():
func_two()</code> <code># two.py
from one import func_one
def do_work():
func_one()
def func_two():
print("Hello, world!")</code> <code># main.py
from two import do_work
do_work()</code>Running main.py produces the error:
<code>% 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)</code>The Python import system executes a module line‑by‑line, creating the module object before its top‑level definitions are evaluated. When two.py imports one.py , one.py in turn tries to import func_two from two before func_two has been defined, leading to the failure.
A simple fix is to import the whole module instead of specific names, postponing the attribute lookup until the function body runs. The revised files look like this:
<code># one.py
import two
def func_one():
two.func_two()</code> <code># two.py
import one
def do_work():
one.func_one()
def func_two():
print("Hello, world!")</code> <code># main.py
from two import do_work
do_work()</code>Running the updated main.py now prints the expected output:
<code>% python main.py
Hello, world!</code>The key idea is that import two (or import one ) defers the name lookup until the function is called, ensuring both modules are fully initialized and avoiding the circular import error.
While restructuring code to eliminate mutual dependencies is the best long‑term solution, this technique can quickly make existing code work.
Original source: https://nedbatchelder.com/blog/202405/one_way_to_fix_python_circular_imports.html
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.