Archtin
All articles
PythonBackend Engineering8 min read

Python 3.15 Is Here: Lazy Imports, frozendict and More

What actually changed in Python 3.15: lazy imports, a built-in immutable frozendict, unpacking inside comprehensions and JIT gains, with code and links to the official docs.

Why this release matters

Python 3.15 was officially released on October 9, 2026. Unlike many version bumps, it brings changes you can see in a few lines of code: explicit lazy imports, a built-in immutable mapping called frozendict, */** unpacking inside comprehensions, and further gains in the experimental JIT compiler.

Lazy imports

Normally a module is loaded the moment its import statement executes. In 3.15 you can mark an import as lazy so the real loading happens only when the name is first used.

import heavy_module
print("Application started")  # heavy_module already loaded
Before: eager import
lazy import heavy_module
print("Application started")  # not loaded yet
heavy_module.run()             # loaded here, on first use
Python 3.15: lazy import
importimport lineload modulecost paid at startup, even if never usedlazy importimport lineload modulecost deferred to first attribute accessfirst use
Where the loading cost lands with eager vs lazy imports

Who benefits

  • CLI tools where most sub-commands never touch most dependencies.
  • Serverless functions and short-lived processes sensitive to cold-start time.
  • Large applications and developer tooling with deep import graphs.
Deferred, not eliminated
Lazy imports don't make code faster by magic. The loading work still happens on first use, and import-time side effects move with it. Measure startup before and after.

frozendict: a built-in immutable dictionary

3.15 adds a dictionary-like type that cannot be modified after creation.

config = frozendict(environment="production", retries=3)

config["retries"] = 5   # TypeError: object does not support item assignment
  • Configuration that must never change at runtime.
  • Hashable mappings (when keys and values are hashable) — usable as dict keys or in sets.
  • Safer sharing of state across modules and threads.

Unpacking inside comprehensions

You can now use * and ** directly in comprehensions, which makes flattening and merging read naturally.

lists = [[1, 2], [3, 4], [5]]
result = [*items for items in lists]
print(result)   # [1, 2, 3, 4, 5]

# Previously
result = [x for items in lists for x in items]
# or: list(itertools.chain.from_iterable(lists))

It's a readability improvement — new syntax, not a performance guarantee.

The JIT keeps improving

The official notes report roughly a 7–8% geometric-mean improvement for the JIT over the standard interpreter on x86-64 Linux, and about 11–12% over the tail-calling interpreter on AArch64 macOS. These are benchmark-suite results, not a promise for every workload.

FeatureWhat it changesCaveat
lazy importWhen a module is loadedCost moves to first use
frozendictImmutable, hashable mappingValues themselves may still be mutable
[*x for x in xs]Unpacking in comprehensionsSyntax only
JITExecution speed on benchmarksExperimental; results vary

Should you upgrade?

Try it in development and CI first. Python 3.15 is very new, so verify that your dependencies, framework and deployment images support it before moving production.

Official documentation

Keep reading

Suggested next articles based on this one.

Design it, don't just read it.

Practise LLD and system design problems with structured rubrics and AI feedback.

Start practising free