Fundamentals 5 min read

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!'.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Resolving Circular Imports in Python by Refactoring Import Statements

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

programmingcode refactoring@Importcircular-import
Python Programming Learning Circle
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.