I know that there are lots of questions about the same import issues in Python but it seems that nobody managed to provide a clear example of correct usage.
Let's say that we have a package mypackage
with two modules foo
and bar
. Inside foo
we need to be able to access bar
.
Because we are still developing it, mypackage
is not in sys.path
.
We want to be able to:
mypackage.foo
foo.py
as a script and execute the sample usage or tests from the __main__
section.How do we have to do the import in foo.py in order to be sure it will work in all these cases.
# mypackage/__init__.py
...
# mypackage/foo/__init__.py
...
# mypackage/bar.py
def doBar()
print("doBar")
# mypackage/foo/foo.py
import bar # fails with module not found
import .bar #fails due to ValueError: Attempted relative import in non-package
def doFoo():
print(doBar())
if __name__ == '__main__':
doFoo()
A relative import specifies the resource to be imported relative to the current location—that is, the location where the import statement is. There are two types of relative imports: implicit and explicit. Implicit relative imports have been deprecated in Python 3, so I won't be covering them here.
Relative imports use dot(.) notation to specify a location. A single dot specifies that the module is in the current directory, two dots indicate that the module is in its parent directory of the current location and three dots indicate that it is in the grandparent directory and so on.
Note that relative imports are based on the name of the current module. Since the name of the main module is always “main”, modules intended for use as the main module of a Python application must always use absolute imports.
In Python, you use the import keyword to make code in one module available in another. Imports in Python are important for structuring your code effectively. Using imports properly will make you more productive, allowing you to reuse code while keeping your projects maintainable.
Take a look at the following info from PEP 328:
Relative imports use a module's
__name__
attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to'__main__'
) then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
When you run foo.py
as a script, that module's __name__
is '__main__'
, so you cannot do relative imports. This would be true even if mypackage
was on sys.path
. Basically, you can only do relative imports from a module if that module was imported.
Here are a couple of options for working around this:
1) In foo.py
, check if __name__ == '__main__'
and conditionally add mypackage
to sys.path
:
if __name__ == '__main__':
import os, sys
# get an absolute path to the directory that contains mypackage
foo_dir = os.path.dirname(os.path.join(os.getcwd(), __file__))
sys.path.append(os.path.normpath(os.path.join(foo_dir, '..', '..')))
from mypackage import bar
else:
from .. import bar
2) Always import bar
using from mypackage import bar
, and execute foo.py
in such a way that mypackage
is visible automatically:
$ cd <path containing mypackage>
$ python -m mypackage.foo.foo
My solution looks a bit cleaner and can go at the top, with all the other imports:
try:
from foo import FooClass
except ModuleNotFoundError:
from .foo import FooClass
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With