Resolve Circular Imports Best Practice
A team is developing a large Python application with multiple modules. They encounter an ImportError when module A tries to import from module B, and module B tries to import from module A. What is the most likely cause and best practice to resolve this?
Quick Answer
The correct answer is to restructure the code to eliminate circular dependencies by extracting shared logic into a third module. This resolves the ImportError because Python’s module initialization is sequential—when module A begins importing module B, and B immediately tries to import the still-incomplete module A, Python raises an error due to the partially loaded namespace. On the PCAP exam, this question tests your understanding of Python’s import system and the principle of dependency inversion, often appearing as a scenario where lazy imports or moving imports inside functions are tempting but inferior solutions. The best practice for circular import resolution is always architectural: isolate the common functionality that both modules need into a separate, independent module that neither depends on. A common trap is thinking that rearranging import statements or using `importlib` fixes the root cause, but these only mask the design flaw. Memory tip: think “triangle to line”—if A and B form a loop, pull the shared code into C to create a clean, one-way dependency chain.
⚠ Common exam trap
Python Institute often tests the misconception that moving imports or using wildcard imports can fix circular dependencies, when in fact only restructuring the code or using lazy imports (as a temporary workaround) addresses the root cause.
Answer choices
Why each option matters
Answer the question above first, then reveal the full breakdown to understand why each option is right or wrong.
Correct answer & explanation
✓
Restructure the code to eliminate circular dependencies by extracting shared logic into a third module.
Circular imports occur when two modules depend on each other at the top level, causing an ImportError due to incomplete module initialization. The best practice is to restructure the code to eliminate the circular dependency, typically by extracting the shared functionality into a third module that both A and B can import without mutual dependence. This approach aligns with Python's module loading mechanism, which executes a module fully before making its names available for import.
Answer analysis
Option-by-option breakdown
For each option: why learners choose it and why it is or isn't the right answer here.
- ✗
Use 'from module import *' to bring all names into the namespace.
Why it's wrong here
Wildcard imports copy every public name into the importing namespace, which neither breaks the circular dependency nor prevents it; the ImportError persists because module B is still partially initialised when A executes. It is tempting for shortening import lists, yet restructuring the modules or importing the specific name inside a function is what resolves the cycle.
- ✗
Use lazy imports (inside functions) to defer the import until runtime.
Why it's wrong here
Lazy imports defer execution but do not remove the circular dependency itself; if the deferred call still runs while the other module is partially initialised, the ImportError recurs. They are tempting because deferring imports genuinely cuts startup cost and breaks some cycles, but the best practise here is refactoring shared code into a third module.
- ✓
Restructure the code to eliminate circular dependencies by extracting shared logic into a third module.
Why this is correct
Circular imports arise when two modules mutually reference each other at import time, so Python cannot fully initialise either. Extracting shared logic into a third module breaks the cycle, satisfying the stem's requirement to resolve the ImportError through restructuring rather than deferred imports or runtime workarounds.
- ✗
Move all imports from module A to the bottom of the file.
Why it's wrong here
Moving imports to the bottom of a file leaves names undefined during module execution and still fails when the other module is only partially initialised. It is tempting because import placement does affect execution order, but Python resolves imports at statement execution, so relocating them merely shifts the failure rather than removing the circular reference.
Visual reference
Go deeper
Related to this question
About these practice questions
Courseiva writes every PCAP question from scratch — 421 in total, each with an explanation and a wrong-answer breakdown. None are copied from real exams or dumps. Learn why practice questions differ from exam dumps →
Same concept, more angles
1 more way this is tested on PCAP
These questions test the same concept from different angles. Work through them to make sure you can recognise it however the exam phrases it.
Variation 1. Which THREE of the following are recommended techniques to avoid circular imports in Python?
hard- A.Manually check sys.modules before importing.
- B.Use the __all__ variable to control what is exported.
- ✓ C.Restructure the code to move shared functionality into a separate module.
- ✓ D.Use lazy imports inside functions or methods.
- ✓ E.Use absolute imports instead of relative imports.
Why C: Option C is correct because circular imports arise when two modules mutually depend on each other at module load time; extracting the shared functionality into a third, dependency-free module breaks the cycle so each module imports only from that neutral module. Option D is correct because deferring the import statement into the body of a function or method means the module is only loaded when that code actually executes, by which point the other module has finished initializing, avoiding the partially-initialized-module problem. Option E is correct because absolute imports (e.g., 'from package.module import name') make the dependency graph explicit and unambiguous, which helps developers detect and refactor cycles that relative imports can obscure. Option A is not recommended because manually inspecting sys.modules is a fragile runtime workaround that does not remove the underlying dependency cycle. Option B is not recommended because __all__ only controls which names are exported by 'from module import *'; it has no effect on when or whether a module is imported, so it cannot prevent circular imports.
JA
Written by Johnson Ajibi, MSc IT Security
Senior Network & Security Engineer · founder of Courseiva
This PCAP practice question is part of Courseiva's free Python Institute certification practice question bank. Courseiva provides original exam-style practice questions with explanations, topic-based practice, mock exams, readiness tracking, and study analytics to help learners prepare for the PCAP exam.