Breaking the Loop: A Deep Dive into Circular Imports in Python
Introduction
Every seasoned Python developer must have seen at least once in their life a circular import issue. You have quickly fixed it via finding a solution from Stackoverflow or AI. However, have you really understood what caused the issue? Yes, two files importing each other is not allowed, but what goes under the hood? Let's discover together..
The Example
The best way to learn is by example. Let's say we have the following 2 files(modules) called module1.py and module2.py,
and these are their contents:
# module1.py
import module2
x = 1
# module2.py
import module1
print(module1.x)Let's run the first module in our terminal:
$ python module1.py
What happens? Let's go through step by step:
- Ok, we are running
module1. Python is going to cache this, in case it will be imported to another module later on. So, it's added tosys.modules.
sys.modules is a dictionary that caches all imported modules, mapping their names to the actual module objects, so Python doesn’t reload them on subsequent imports.- When the code is at
import module2step(line 2), python starts importingmodule2. We can also say: Python executes the top level code ofmodule2
Top-level code is the part of a Python file that runs immediately when the file is executed or imported, outside of any function or class definitions.
So, any class or function definitions will be run but not the contents inside!, and also print statements are run in the import process.
- Now we are at line 6, in
module2starting to importmodule1.py. You would think Python would start theimport aprocess, but no. On step 1,module1has already been cached. Therefore, it continues. - Now, we are at
print(module1.x)line 7. Ok, Python knows thatmodule1is cached. So, let's use that.module1.x?? There is noxundermodule1. Remember thatx = 1hasn't run yet! - Python throws
AttributeErrormeaning I can't findxattribute undermodule1.
Benefits of Caching Modules
Imagine Python wasn't caching modules. Then everytime a module is imported, top level code would run. Python saves time and processing power by caching modules.
Also at step 3, we covered that code continues. If cashing was not implemented, then infinite loop would occur. That's being avoided.
Conclusion
Now you have a better understanding of circular imports. Hopefully, you won't need to search for helpers like stackoverflow, AI but resolve with your grasp on the topic.
Also, understanding circular imports might come handy in interviews ;)