Python Modules & Packages
Python's module system is how code is organized and reused. A module is any .py file. A package is a directory containing an __init__.py file. Understanding how Python finds and loads code is essential for building larger projects.
The import Statement
The simplest way to use another module:
When you write import math, Python:
- Searches
sys.pathfor a file or package namedmath - Executes the module's code (once โ subsequent imports use the cached version in
sys.modules) - Binds the name
mathin your local namespace to the module object
from module import name
Import specific names directly into your namespace:
The wildcard form from math import * imports all names not starting with underscore, but this is discouraged because it pollutes your namespace and makes it hard to tell where names come from.
Aliasing with as
Rename imports to avoid conflicts or shorten long names:
The as keyword works for both import and from ... import:
The __name__ Variable
Every Python module has a built-in __name__ attribute. When a module is run directly as a script, __name__ is set to "__main__". When imported by another module, __name__ is the module's file name (without .py).
This enables the classic guard pattern:
This pattern lets a file be both an importable module and a runnable script. It's one of Python's most important idioms.
Packages: Modules in Directories
A package is a directory containing an __init__.py file (which can be empty):
Import from the package:
__init__.py: The Package Initializer
__init__.py runs when the package is imported. You can use it to:
- Re-export names for a cleaner public API:
-
Run initialization code (connect to DB, load config, etc.)
-
Define
__all__to control whatfrom package import *exports
Relative Imports
Inside a package, you can use relative imports with dot notation:
Relative imports only work inside packages โ not in top-level scripts. They make refactoring easier since package internals don't need to know the package's name.
__all__: Controlling Public API
Define __all__ as a list of strings to specify what names are exported:
When someone does from utils.strings import *, only the names in __all__ are imported.
sys.path: How Python Finds Modules
Python searches for modules in this order:
- The directory of the script being run (or current directory in the REPL)
- Directories in the
PYTHONPATHenvironment variable - Installation-dependent default locations (site-packages, stdlib)
You can inspect and modify sys.path at runtime:
importlib: Dynamic Imports
For dynamic imports (when you don't know the module name until runtime), use importlib:
This is used internally by plugin systems, dependency injection frameworks, and test runners.
Module Caching
Python caches imported modules in sys.modules. Subsequent import statements for the same module return the cached version without re-executing the module file:
Best Practices
- Group imports: stdlib first, then third-party, then local imports
- Use absolute imports in application code for clarity
- Use relative imports inside packages for internal references
- Always use the
__name__ == "__main__"guard in scripts - Define
__all__in modules that are meant to be public APIs - Avoid circular imports โ restructure code if modules import each other
Knowledge Check
What is the value of `__name__` when a Python file is run directly as a script (e.g., `python myscript.py`)?
What is the purpose of `__init__.py` in a Python package directory?
In a relative import like `from ..utils import helper`, what does `..` mean?