Python 3.15: import only when needed

Importing a library can involve reading files, executing initialization code, and loading further dependencies. We pay that cost even when we only want to display a tool’s help message. Python 3.15 introduces Explicit Lazy Imports, defined in PEP 810 and covered in What’s New in Python 3.15: we can declare an import and defer loading until first use.
A small example
This script requires Python 3.15 or later and uses only the standard library:
# tool.py
import argparse
lazy import zoneinfo
parser = argparse.ArgumentParser()
parser.add_argument("--zone", help="Look up an IANA time zone")
args = parser.parse_args()
if args.zone:
print(zoneinfo.ZoneInfo(args.zone)) # zoneinfo loads here
else:
print("No time zone lookup requested")
With python tool.py --help, the tool exits without loading zoneinfo, provided another dependency has not already imported it. With python tool.py --zone Europe/Madrid, accessing zoneinfo triggers loading; this lookup requires system time zone data or the tzdata package.
There is also lazy from zoneinfo import ZoneInfo. Loading is deferred for the whole module; Python does not execute just the requested class. The language reference restricts this syntax to module scope: it is not allowed inside functions, classes, or try blocks, or with import *.
Where work is saved
- Faster startup: dependency initialization is postponed until an execution path needs it.
- Less unnecessary CPU work and I/O: a tool with a charting option could avoid loading a heavy visualization library when it only exports text.
- Lower memory use: modules that never load do not create their objects or initialize dependencies used exclusively by them.
Savings depend on the program. If we eventually use every module, much of the cost simply moves to a different moment. Compare fresh processes, measuring startup, the first operation, and memory under representative workloads; there is no universal improvement percentage.
The costs of postponing imports
Slower first access: the first operation needing the module pays for its import. A service might benefit from preloading it before accepting requests to avoid that initial latency.
Errors surface later: a missing dependency or initialization failure may go unnoticed until the relevant feature runs. Test optional paths too, and catch errors around usage where appropriate. Import side effects, such as plugin registration, are also delayed.
Static typing needs compatible tools: according to the PEP, an analyzer can resolve these names as it would an ordinary import; laziness does not make the type unknown. The new syntax does require support from the analyzer and formatter. Static analysis also differs from execution: a framework evaluating annotations may trigger module loading. lazy import and imports guarded by TYPE_CHECKING have different runtime behavior.
For compatibility with older versions, use __lazy_modules__ = ["zoneinfo"] followed by import zoneinfo: it becomes lazy from 3.15 onward and remains eager in earlier versions. The lazy syntax raises a syntax error in Python 3.14 and earlier.
