A circular import happens when two or more modules depend on each other directly or indirectly during the import process.
In simple words:
Module A imports Module B
Module B imports Module A
This creates a loop (cycle) → Python gets stuck or gives errors.
When Python imports a module:
It creates a module object and places it in sys.modules
.
It executes the code inside the module.
If another import happens inside it, Python checks sys.modules:
If module exists but is not yet fully executed, Python may give partially initialized module errors.
👉 That’s why circular imports often raise:
ImportError: cannot import name 'X' from partially initialized module
a.py
print("Loading a.py")
import b # a.py imports b.py
def func_a():
print("Function A")
b.py
print("Loading b.py")
import a # b.py imports a.py
def func_b():
print("Function B")
main.py
import a
✅ What happens:
Python loads a.py
→ sees import b
.
It starts loading b.py
→ sees import a
.
But a
is already being loaded, so Python provides a partially initialized a
.
If you try to use something in a
that isn’t yet defined, you get an error.
Partially initialized module:
ImportError: cannot import name 'func_a' from partially initialized module 'a'
Sometimes works but gives unexpected behavior because of incomplete definitions.
✅ 1. Reorganize Imports (Best Practice)
Move shared functions/classes into a third module that both can import.
Example:
a.py
and b.py
both import from common.py
.
✅ 2. Local Imports (inside functions)
Instead of top-level imports, import only when needed.
# b.py
def func_b():
from a import func_a
func_a()
print("Function B")
✅ 3. Use import module
instead of from module import X
This avoids early evaluation of attributes.
# Instead of
from a import func_a
# Do this
import a
def func_b():
a.func_a()
✅ 4. Refactor Code Structure
Sometimes circular imports mean the code design is tightly coupled.
Breaking it into layers (utilities, services, models, controllers, etc.) helps.
If modules don’t access each other’s values during import, Python can resolve it.
Example: both modules just import each other but use them only after program startup.
Think of circular import like two people trying to shake hands at the exact same time.
Each one is waiting for the other → results in a deadlock or incomplete action.
✅ Summary:
Circular import = mutual dependency between modules.
It causes ImportError or unexpected behavior.
Fix by restructuring code, using local imports, or splitting common logic into a new module.