Explain Codes LogoExplain Codes Logo

How can I do relative imports in Python?

python
relative-imports
python-modules
package-structure
Nikita BarsukovbyNikita Barsukov·Sep 14, 2024
TLDR

Execute a relative import with the prefix . for same-level modules or .. for a parent-level module in a package. Use them in your Python code like:

from .sibling_module import sibling_function # Staying in the same room!

Or to import from a parent package:

from ..parent_module import parent_function # Going upstairs to borrow sugar!

Ensure you're inside a package and not running the script as __main__ for these relative imports to work.

Unlocking the 'Attempted relative import in non-package' mythical riddle

The mysterious error "Attempted relative import in non-package" visits those who dare to perform a relative import from a module deemed as __main__. It feasts on empty __init__.py files or attempts to import from a script ran directly, leaving behind a trail of confusion and unanswered StackOverflow questions. Remember, Python recognizes directories as packages if they contain an __init__.py file.

The 'python -m' spell

To run a module within a package without angering the Python gods, invoke the python -m spell in the command line. This charm tells Python to run the module as a script, enabling successful relative imports like a smooth operator:

python -m package.subpackage.module # Here's a `python -m` charm to pacify the Python gods!

This is the prophesized way to avoid errors related to running a module as __main__.

Invoking PEP 366

In some ancient scripts, it is told of the PEP 366, a mystical guideline that enables executing a module inside a package as if it were run with the -m switch. This works by setting the __package__ attribute — a viable option for dealing with stubborn legacy codebases.

The secret life of the module's __name__

With relative imports, the module's __name__ changes, affecting the module's position within the package. If you run a script directly, its __name__ changes to __main__, complicating relative imports. Hence, the deception of -m, which makes Python think it's a script and not __main__.

Captain's guide to organising your package

Ensuring a healthy package structure can be more effective than a daily apple for keeping import headaches away. Avoid filling your package directories with scripts you'll be running directly. Rather, let a main.py reside outside your packages, taking charge of coordinating the imports and running the show. Your imports will thank you.

How to catch an ImportError

Embrace your inner catcher and catch ImportError gracefully. You do that by wrapping relative imports in try-except blocks. If you catch an ImportError, you can provide helpful feedback or fall back to an absolute import, if necessary. This way, even failing is done in style.

try: from . import module except ImportError: # You caught an ImportError! Now do something about it.

The clean imports mantra

And finally, to keep the house (okay, the namespace) clean, resist the seductive pull of "from module import *". Explicit is better than implicit, remember? Namespace pollution isn't a fun party. Keep your imports explicit and your code easy to maintain.