Modules & Packages Competent¶
When you'd use this
Organising Python code into modules, packages and namespaces.
Organize a growing codebase: split a long script into modules, group modules into packages, and expose a clean public API with __init__.py and __all__.
Modules¶
A single .py file of reusable code you import elsewhere. Use modules to split a growing script into focused, testable units.
A module is just a .py file. Import it by name.
# main.py
import math_utils
print(math_utils.add(2, 3))
from math_utils import add, PI
print(add(2, 3))
from math_utils import add as addition # alias
Packages¶
A directory of modules (with __init__.py) that groups related code under one namespace. Use packages to organize a library or app into subsystems.
A package is a folder with an __init__.py file.
myproject/
├── __init__.py
├── core/
│ ├── __init__.py
│ └── engine.py
└── utils/
├── __init__.py
└── helpers.py
__init__.py¶
Runs when the package is imported — use it to expose a clean public API and set __all__ so from pkg import * only pulls intended names.
# myproject/__init__.py
from .core.engine import start
from .utils.helpers import format_name
__all__ = ["start", "format_name"] # controls "from myproject import *"
The if __name__ == "__main__" guard¶
Make a file work both as an importable module and a runnable script — the guarded code runs only on direct execution, not on import.
This ensures code runs only when the file is executed directly, not when imported.
Import forms, compared¶
Import forms, compared in Modules & Packages — what it is and when to use it.
import math # math.sqrt(4)
from math import sqrt # sqrt(4)
from math import sqrt as sr # sr(4)
from math import * # pulls public names (avoid in libraries)
Relative vs absolute imports¶
Relative vs absolute imports in Modules & Packages — what it is and when to use it.
Inside a package, relative imports use leading dots:
# absolute (preferred for clarity)
from myproject.utils.helpers import format_name
# relative (within the same package)
from .helpers import format_name # same package
from ..core.engine import start # parent package
Relative imports only work inside a package (a module run as a script can't use them).
What import actually does¶
A core question explored in Modules & Packages: What import actually does.
The first import executes the module top to bottom and caches it in sys.modules; later imports reuse the cached module (the body does not re-run):
import sys
import json # importing a second time...
import json # ...reuses the cached module object
print("json" in sys.modules) # True
Inspecting a module¶
Inspecting a module in Modules & Packages — what it is and when to use it.
import math
print(math.__name__) # math
print("sqrt" in dir(math)) # True
print(type(math).__name__) # module
Namespace packages (no __init__.py)¶
Namespace packages (no init.py) in Modules & Packages — what it is and when to use it.
Since PEP 420, a directory without __init__.py can still be an importable namespace package, letting one logical package span multiple directories. Prefer a regular package (with __init__.py) unless you specifically need this.
Practice exercises¶
- Split a single-file program into 3 modules and import between them.
- Create a package with
__init__.pythat exposes a clean public API. - Write a module that works both as an importable library and a CLI script.
💬 Discussion
Have a question about this topic? Found an error? Share your thoughts below.