Understanding Python's Import Context
Importing modules in Python can be confusing. It works, but many people don't understand why it works.
A typical scenario: We encounter a ModuleNotFoundError
, search online, and eventually we find a workaround or a way to make it work, but we don't understand why it works! pytest documentation wisely emphasizes this aspect.
The root cause of the confusion
The root cause of the confusion is how you run a Python file, which affects the Python module search path. In other words, it affects sys.path
, the directory Python uses to search for modules.
Let's look at the example below, with the files main.py
and sub/b.py
:
project
└───sub
│ │ __init__.py
│ │ a.py
│ │ b.py
│ └───subsub
│ │ c.py
└───main.py
# main.py
from sub.b import func_b
if __name__ == "__main__":
import sys
print(sys.path)
print("Hello World!")
func_b()
# module sub/b.py
from a import func_a1 # This should Fail if you run python main.py
def func_b():
print("call func_b() in b.py")
func_a1()
import sys
print(sys.path)
if __name__ == "__main__":
func_b()
If you now run python main.py
, guess what? It will fail at this line in the file sub/b.py
.
from a import func_a1
(pythopn-snippet-py3.10) (base) hai@hai-virtual-machine:~/Dev/pythopn-snippet/import_play$ python main.py
Traceback (most recent call last):
File "/home/hai/Dev/pythopn-snippet/import_play/main.py", line 1, in <module>
from sub.subsub.c import func_c
File "/home/hai/Dev/pythopn-snippet/import_play/sub/subsub/c.py", line 2, in <module>
from ..b import func_b # also work
File "/home/hai/Dev/pythopn-snippet/import_play/sub/b.py", line 2, in <module>
from a import func_a1 # This should Fail if you run python main.py
ModuleNotFoundError: No module named 'a'
Hmm, why? Module "a" is in the same folder as module "b." Why does Python say "No module named 'a'?
This is because sys.path
doesn't include the sub/
folder in its search path. The module named "a" only makes sense in the sub/
folder; it doesn't make sense elsewhere.
When you run python main.py
, sys.path
will include the current folder of main.py
, which is the project folder. In the project folder, there is no module a.py, so it will fail.
Let's make a small change to b.py
.
from sub.a import func_a1
from .a import func_a1
Now, running python main.py
should be successful.
Explanation: sys.path
includes the project
folder, which contains the sub
package.
So, it will be able to find sub.a
! The second import line uses a relative import. Since a.py
and b.py
are in the same directory, it will work.
Sample code used in this article: python-note/import_play at main · nguyenhaiquoc/python-note (github.com)
Subscribe to my newsletter
Read articles from hai nguyen directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by