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.pyIn 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 moduleIn outter.py, we write
from package import innerNow 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.
run python outter.py, you should get
----inner.py--- __name__ is package.inner ----module.py---- __name__ is package.sub.module I'm in module.pyThis 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
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-packageThis 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.
run python -m package.inner, you should get
----inner.py--- __name__ is __main__ ----module.py---- __name__ is package.sub.module I'm in module.pyThis works because the -m switch tells python to load inner as a module, not as a top-level script.
