Python Circular Imports Module: Solving Circular Import Problem
In python, a module can be made by importing other modules. In some cases, a Circular dependency is created. Circular dependency is the case when some modules depend on each other. It can create problems in the script such as tight coupling, and potential failure.
To run the script without any error the Circular dependency has to be removed. Circular dependency can be created in many ways. With a well-designed program, these cases can be avoided.
Python Circular Imports:
Circular importing is a conceptual error that is formed due to circular dependency created with the import statement in our Python program.
- Python Circular Imports is a type of Circular dependency. It occurs in python when two or more models import each other and it repeats the importing connection into an infinite circular call.
- With Circular Imports, the python script gives an error. To run the python script it has to be removed and it is very difficult to find and remove the script manually.
- Circular imports are created because of the bad coding design and implementation-related logical anomalies.
Here is a situation shown using three separate programs:
Explanation:
Here, we have defined three programs. 2 of them are treated as modules. In the first code function1() has been called that has a module2.function2() within itself as the calling function. The function2() of module 2 again has a print() and function3 and the body of function3() has some print().
Finally, the __init__.py is calling module1.function1() . This circular calls of importing statements and their associated function will create an error.
NOTE:
When Python performs the importing of a module, it verifies and goes through the module registry to identify if the module was already imported. In case the module has already been there registered, Python uses its existing object from cache.
The module registry is nothing but a data structure or tabular structure containing information about multiple imports (predefined and user defined) of modules that were initialized and indexed with module name(s) . Developers can access this table using sys.modules .
Fixing Circular Imports in python
There are many ways by which Circular imports can be avoided. Some of them are:
- Change Name of Working python script
- Import the module
- Avoid Circular Import
- Merge modules
- Import when need
Change Name of working python script:
Changing the name of the Working file different from the module which is imported in the script can avoid the Circular Imports problem.
Import the module:
Avoid importing objects or functions from a module that can cause Circular Imports. It is good to import the whole module to avoid the Circular Import.
Avoid Circular Import:
There are many cases where one module function depends on another model which in turn depends on it. This case mostly creates Circular Imports.
Merge modules:
When one module depends on another model and that module depends on first then it is good practice to Merge both the modules to avoid Circular Imports.
Program:
Explanation:
Here, merge both module1 and module2, so that the user-defined functions within them come under one module.
Import when need:
The python module can be imported anywhere in the program. It is not necessary for python to first import the module and start working over it. Importing the module before one line where it is needed to avoid Circular Import.
Program:
Explanation:
Here, In function one imports the newmodule before calling the fun3.
Conclusion:
We hope this article has given a crisp idea on how to stay ahead of circular import issue. We learn about Circular dependency, Circular Imports, and various ways to solve the problem of Circular imports. Circular Imports reduce code reusability and create infinite recursions leading to inefficient programming and memory leaks, and can even lead to cascade effects. It is good programming practice to avoid Circular Imports.
Python Circular Imports

A circular dependency occurs when two or more modules depend on each other. This is due to the fact that each module is defined in terms of the other (See Figure 1).
The code above depicts a fairly obvious circular dependency. functionA() calls functionB() , thus depending on it, and functionB() calls functionA() . This type of circular dependency has some obvious problems, which we'll describe a bit further in the next section.
Problems with Circular Dependencies
Circular dependencies can cause quite a few problems in your code. For example, it may generate tight coupling between modules, and as a consequence, reduced code reusability. This fact also makes the code more difficult to maintain in the long run.
In addition, circular dependencies can be the source of potential failures, such as infinite recursions, memory leaks, and cascade effects. If you're not careful and you have a circular dependency in your code, it can be very difficult to debug the many potential problems it causes.
What is a Circular Import?
Circular importing is a form of circular dependency that is created with the import statement in Python.
For example, let's analyze the following code:
When Python imports a module, it checks the module registry to see if the module was already imported. If the module was already registered, Python uses that existing object from cache. The module registry is a table of modules that have been initialized and indexed by module name. This table can be accessed through sys.modules .
If it was not registered, Python finds the module, initializes it if necessary, and executes it in the new module's namespace.
In our example, when Python reaches import module2 , it loads and executes it. However, module2 also calls for module1, which in turn defines function1() .
The problem occurs when function2() tries to call module1's function3() . Since module1 was loaded first, and in turn loaded module2 before it could reach function3() , that function isn't yet defined and throws an error when called:
How to Fix Circular Dependencies
In general, circular imports are the result of bad designs. A deeper analysis of the program could have concluded that the dependency isn't actually required, or that the depended functionality can be moved to different modules that wouldn't contain the circular reference.
A simple solution is that sometimes both modules can just be merged into a single, larger module. The resulting code from our example above would look something like this:
However, the merged module may have some unrelated functions (tight coupling) and could become very large if the two modules already have a lot code in them.
So if that doesn't work, another solution could have been to defer the import of module2 to import it only when it is needed. This can be done by placing the import of module2 within the definition of function1() :
Free eBook: Git Essentials
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
In this case, Python will be able to load all functions in module1 and then load module2 only when needed.
This approach doesn't contradict Python syntax, as the Python documentation says: "It is customary but not required to place all import statements at the beginning of a module (or script, for that matter)".
The Python documentation also says that it is advisable to use import X , instead of other statements, such as from module import * , or from module import a,b,c .
You may also see many code-bases using deferred importing even if there isn't a circular dependency, which speeds up the startup time, so this is not considered bad practice at all (although it may be bad design, depending on your project).
Wrapping up
Circular imports are a specific case of circular references. Generally, they can be resolved with better code design. However, sometimes, the resulting design can contain a large amount of code, or mix unrelated functionalities (tight coupling).
Have you run in to circular imports in your own code? If so, how did you fix it? Let us know in the comments!
Python Type Hinting : Eliminating ImportError Due to Circular Imports
We all face sometimes ImportError due to having a circular import that is occurred only for type hinting. There is a simple way to handle this kind of problem.
Let’s take we have two files like following:
Exit fullscreen mode
The another file:
Exit fullscreen mode
These two files will work fine as we did not added any type hinting. Only the BookManager is imported in book_model.py file. But if we add type hint to the create_new_version_of_book method from BookManager , then it will be as follows:
Exit fullscreen mode
Here now we will get an ImportError due to circular import and we will get something like the below message when we want to run the project/files:
Exit fullscreen mode
The solution is using typing.TYPE_CHECKING constant as below:
Exit fullscreen mode
Line 1: We have imported annotations. __future__.annotations is not default in Python now; but it will become the default in Python 3.11. For details, you have a look into PEP 563. If you don’t import it, you can use type hints as string. In our case, it will be old_book_object: ‘Book’ .
Line 3: We have imported typing.TYPE_CHECKING special constant. The value of the constant is always False, but set to True by any type checkers, such as Mypy. We have used the this constant to make our import conditional.
Line 7–8: We have imported the model with a condition, so it will only be imported when the TYPE_CHECKING variable is True.
Python Circular Imports
What is a Circular Dependecy ?
When two or more modules rely on each other, this is referred to as a circular dependency. This is due to the fact that each module is defined in terms of the others.
Python cyclic imports
Let’s take these two python files as an example
# file1
import file2
def printName(): print(«OWC»)
printName()
# file2
import file1
def printAddress(): print(«Kigali»)
printAddress()
Once that’s done, let’s try running file1.py, It worked like a charm right.
The output:
OWC
Kigali
OWC
The above output seems legit, however, let’s make a small change to our code.
Let’s import the functions particularly instead of the whole file.
# file1
from file2 import printAddress
def printName():
print(«OWC»)
printName()
printAddress()
# file2
from file1 import printName
def printAddress():
print(«Kigali»)
printAddress()
printName()
When we try to run `python3 file1.py` we get this error in our terminal
«` Traceback (most recent call last): File «file1.py», line 1, in <module> from file2 import printAddress File «/Users/bruce/workspace/journals/file2.py», line 1, in <module> from file1 import printName File «/Users/bruce/workspace/journals/file1.py», line 1, in <module> from file2 import printAddress ImportError: cannot import name ‘printAddress’ from partially initialized module ‘file2’ (most likely due to a circular import) (/Users/bruce/workspace/journals/file2.py) «`
Python is showing us an «`ImportError «` and that it is most likely due to a circular import
To avoid circular import errors, you can move «`from module import X«` to a place it is needed.
For example
# file1
def printName():
print(«OWC»)
printName()
from file2 import printAddress
printAddress()
# file2
def printAddress():
print(«Kigali»)
printAddress()
from file1 import printName
printName()
Wrapping up
Circular imports are a specific case of circular references. In general, they can be solved through better code design. However, the resulting design may contain a large amount of code or may combine unrelated functionalities (tight coupling)