Modules & Packages
Split code across files, import it in several ways, make a file that works both as a library and as a script, and group modules into packages.
You will learn
- What a module is, and the different forms of
import. - The
if __name__ == "__main__":idiom. - Where Python looks for modules (
sys.path), and what__pycache__is. - Packages,
__init__.py, relative imports, and__all__. - New in 3.15:
lazy importfor faster start-up.
What is a module?
A module is simply a .py file. Its name is the file name without .py. Everything defined at the top level of the file (functions, classes, variables) becomes an attribute of the module.
Analogy: a module is a toolbox with a label on it. import fibo brings the whole box onto your desk, and you take tools out with fibo.fib.
Start the REPL in the same folder and import it:
Each module has its own namespace (its own set of names). A variable called total inside fibo never clashes with your total. Inside a module, __name__ holds the module's name.
Forms of import
| Statement | What you can then write | Notes |
|---|---|---|
import fibo | fibo.fib(10) | the clearest; you always see where a name comes from |
import fibo as fb | fb.fib(10) | a short alias, e.g. import numpy as np |
from fibo import fib, fib2 | fib(10) | copies just those names into your namespace |
from fibo import fib as fibonacci | fibonacci(10) | renames on import |
from fibo import * | fib(10), fib2(10) | imports all public names (no leading _); avoid in real code |
โ Star import hides where names come from
โ Explicit is better than implicit
โ Java: import in Python runs the module's code the first time (like a static initialiser). There is no classpath: Python searches a list of folders (see sys.path below). ๐จ JS: from fibo import fib is like import { fib } from './fibo.js'. Python has no export keyword: every top-level name is importable.
A module's top-level code runs only once per process, the first time it is imported. Later imports reuse the cached module object stored in sys.modules. When you edit a module during a REPL session, either restart the REPL or use importlib.reload(fibo).
Executing modules as scripts
When you run a file directly (python3 fibo.py), its __name__ is set to "__main__" instead of "fibo". So you can add code that runs only when the file is run as a script, and not when it is imported:
๐ก Tip: the usual layout is to put the work in a main() function and call it from the guard. Then tests can import the file without side effects, and the file still works as a command.
The module search path
When you import spam, Python first checks for a built-in module with that name. Then it searches the folders listed in sys.path, in order:
- The folder containing the script you ran (or the current folder in the REPL).
- The folders in the
PYTHONPATHenvironment variable, if it is set. - The installation defaults: the standard library, then
site-packages(wherepipinstalls packages).
โ ๏ธ The shadowing pitfall. The script's own folder comes first. If you name your file random.py, json.py or email.py, then import random loads your file instead of the standard library's, and you get confusing errors like AttributeError: module 'random' has no attribute 'randint'. Never name your files after standard modules.
"Compiled" Python files
To speed up loading, Python caches a compiled version of each imported module in a __pycache__ folder, with names like fibo.cpython-315.pyc. The .pyc file holds bytecode: a lower-level form of your code that the interpreter runs.
- Python checks the source file's modification time and recompiles automatically when the file changes. You never manage these files yourself.
- A
.pycfile loads faster, but it doesn't run any faster. - The script you run directly is not cached; only imported modules are.
- Add
__pycache__/to.gitignore.
๐ Changed in 3.15: the old module.__cached__ attribute is no longer set. Use module.__spec__.cached if you ever need the .pyc path.
Standard modules and dir()
Python ships with a large standard library of modules (Chapters 13โ14). Some, like sys, are built into the interpreter itself.
The built-in dir() lists the names a module defines. With no argument, it lists the names you have defined in the current scope.
Notice fib2 is still listed, even though the second version of fibo.py removed it. This REPL session imported the first version, and a module is loaded only once. Even importlib.reload() re-runs the file into the same module object, so old names stay behind. When in doubt, restart the REPL.
Packages
A package is a folder of modules, which gives you dotted names like sound.effects.echo. Mark a folder as a package by putting an __init__.py file in it. The file can be empty; its code runs when the package is first imported.
The three ways to import from it:
A real, runnable package
Here is a tiny shop package that you can create and run:
- Relative imports start with a dot:
from . import x(this package) orfrom ..sub import y(the parent package). They only work inside a package. - Run a module that lives in a package with
python3 -m shop.cart, from the folder that containsshop/. Runningpython3 shop/cart.pydirectly fails, because the file then isn't part of any package, so the relative import has nothing to be relative to.
__all__ and import * from a package
from package import * does not scan the folder for submodules. It imports exactly the names listed in the package's __all__. Without __all__, it only imports the names already defined in __init__.py. Inside a plain module, __all__ limits what import * exports in the same way.
๐ Namespace packages: a folder without __init__.py can still be imported, as a "namespace package", which is used for splitting one package across several locations. Beginners should always add __init__.py; it avoids surprises.
Lazy imports (new in 3.15)
Large programs import many modules at start-up, even ones that are rarely used, and that slows down command-line tools. PEP 810 adds the soft keyword lazy. A lazy import binds the name immediately, but loads the module only the first time the name is used.
lazyis only allowed at the module's top level. Inside a function it is aSyntaxError(an import inside a function is already lazy).lazy from module import *is not allowed.- It is a soft keyword, so old code that uses
lazyas a variable name still works. - Errors from a lazy import (such as a missing module) appear at first use, not at the import line.
๐ Older pattern you'll still see: before 3.15, people delayed imports by putting the import statement inside the function that needed it. That still works and is still fine for one-off cases.
More examples
1. A config module shared by other files
2. Which file did a module come from?
3. Aliasing a long module name
4. Choosing an implementation at import time
(try/except is covered in Chapter 10.)
5. A package's public API through __init__.py
Recap
- A module is a
.pyfile. Preferimport moduleorfrom module import name, and avoidimport *. - A module's code runs once, at its first import.
if __name__ == "__main__":guards script-only code. sys.pathstarts with the script's folder, so don't name files after standard modules.- A package is a folder with
__init__.py. Inside it, use relative imports (from . import x), and run its modules withpython3 -m pkg.mod. lazy import x(3.15) delays loading until first use, and only works at the top level.
Exercises
Exercise 1 ๐ข easy build it ยท playground
Create playground/mathx.py with a function square(n). In playground/use_mathx.py, import it in two different ways and print square(7).
Show solution
Run python3 use_mathx.py from inside playground/. The script's own folder is the first entry in sys.path.
Exercise 2 ๐ข easy predict the output
greet.py contains print("loading greet") and nothing else. What does import greet; import greet print?
Show solution
loading greet, only once. The second import finds the module already in sys.modules and doesn't run it again.
Exercise 3 ๐ข easy explain why
Your file is called random.py and does import random; print(random.randint(1, 6)). It fails with AttributeError. Why?
Show solution
import random finds your random.py first, because the script's folder comes first in sys.path. Your file doesn't define randint. Rename the file and delete the __pycache__ entry for it.
Exercise 4 ๐ข easy fix the bug
Running tests.py, which does import calc, prints a large demo every time. Fix calc.py.
Show solution
When the file is imported, __name__ is "calc", so the demo is skipped.
Exercise 5 ๐ก medium predict the output
Given the shop package above, which of these work from the folder containing shop/? (a) python3 -m shop.cart (b) python3 shop/cart.py (c) python3 -c "from shop.cart import total; print(total([1]))"
Show solution
(a) and (c) work. (c) prints 1.2. (b) fails with ImportError: attempted relative import with no known parent package, because running a file by its path makes it __main__ with no package around it.
Exercise 6 ๐ก medium refactor it
Replace the star imports with explicit imports: from math import * and from statistics import *, where the code uses sqrt, pi and mean.
Show solution
Readers (and linters) can now see where each name comes from. It also avoids accidental clashes: both modules have names that could overlap.
Exercise 7 ๐ก medium build it ยท playground
Turn playground/money.py (Chapter 5) into a script and a module: importing it should print nothing, and python3 money.py 1234 should print โฌ12.34.
Show solution
Passing argv into main makes the function easy to test: you can call main(["x", "5"]) from a test.
Exercise 8 ๐ก medium explain why
Why is lazy import json inside a function a SyntaxError? What would you write instead?
Show solution
A plain import inside a function already runs only when the function is called, so it is lazy already. The lazy keyword exists to delay top-level imports, which normally run at start-up. Inside a function, just write import json.
Exercise 9 ๐ด hard predict the output
pkg/__init__.py contains print("init") and from .a import x. pkg/a.py contains print("a") and x = 1. pkg/b.py contains print("b"). What does python3 -c "import pkg.b" print?
Show solution
init, a, b, in that order. Importing pkg.b first imports pkg, which runs __init__.py, and that imports a. Then b runs.
Exercise 10 ๐ด hard mini-project ยท playground
Create a package playground/tracker/ with __init__.py, models.py (a function make_expense(amount, category) that returns a dict), report.py (a function total(expenses)) and __main__.py, so that python3 -m tracker (run from playground/) prints a small demo total. Re-export both functions in __init__.py and set __all__.
Show solution
A package's __main__.py is what python3 -m package runs. Chapter 16 grows this package into the full project.
Official docs
- 6. Modules (scripts, search path, compiled files, standard modules, dir(), packages)
- PEP 810: Explicit lazy imports
- Language reference: the import system