Python relative import is a topic that bugs me for a long time. Today, I came across these two articles which helped me understand the topic better.
The links are here for reference:
(https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time)
(https://www.blog.pythonlibrary.org/2016/03/01/python-101-all-about-imports/)
The main point to take away is that
When you run a module directly from command line, it’s name was set to
__main__
. That will cause the relative imports within the module to fail, because__main__
does not reveal it is in a package.
To better explain it, I will use an example. Assume we have the following structure
|--package
| |--__init__.py
| |--sub
| |--__init__.py
| |--module.py
| |--inner.py
|--outter.py
In module.py
, we write
print "----module.py----"
print "__name__ is %s" % __name__
print "I'm in module.py"
In inner.py
, we write
print "----inner.py---"
print "__name__ is %s" % __name__
from .sub import module
In outter.py
, we write
from package import inner
Now cd
to the parent directory of the package
directory.
All the tests are conducted on Windows. You may adjust the command if you are on Linux.
Test 1
run python outter.py
, you should get
----inner.py---
__name__ is package.inner
----module.py----
__name__ is package.sub.module
I'm in module.py
This works because python will add the current directory to its search path, so python can find the package
, and both inner.py
and module.py
retains package information
Test 2
run python package\inner.py
, you should get
----inner.py---
__name__ is __main__
Traceback (most recent call last):
File "package\inner.py", line 3, in <module>
from .sub import module
ValueError: Attempted relative import in non-package
This is because when the inner.py
was executed in command line directly, it’s name was set to __main__
. So it lost the package information and was not considered in a package.
Test 3
run python -m package.inner
, you should get
----inner.py---
__name__ is __main__
----module.py----
__name__ is package.sub.module
I'm in module.py
This works because the -m
switch tells python to load inner
as a module, not as a top-level script.