Python 3.15 Deep Dive: Explicit Lazy Imports and Native frozendict Drive Major Performance Gains

Python · Release 3.15

Python 3.15 Deep Dive: Explicit Lazy Imports and Native frozendict Drive Major Performance Gains

pythonPythonReleaseLazy ImportsfrozendictPerformance Optimization

Sources:GitHub Releases + 官方博客 + HN

Python 3.15 was officially released on October 9, 2026. This release introduces significant improvements across the language syntax and performance optimizations. Explicit lazy imports address the startup bottlenecks of large-scale applications, the introduction of frozendict fills the longstanding void for immutable mappings, unpacking in comprehensions makes data transformations much cleaner, and a stable ABI for free-threaded builds accelerates Python’s multi-threaded future. Here is an in-depth breakdown of the most critical new features in this release.

PEP 810: Explicit Lazy Imports

Large Python applications frequently suffer from slow startup times, largely due to cascading module imports. Historically, developers deferred imports inside functions or relied on importlib for on-demand loading. Python 3.15 provides a cleaner, first-class solution: explicit lazy imports enabled by the lazy soft keyword.

When using lazy import, Python avoids loading the module immediately. Instead, it creates a lightweight proxy object, deferring actual module execution until an attribute on the imported module is accessed for the first time.

lazy import json
lazy from pathlib import Path

print("Starting up...")  # json and pathlib are not yet fully imported

data = json.loads('{"key": "value"}')  # json is loaded here upon first access
p = Path(".")  # pathlib is loaded here upon first access

If a lazily imported module does not exist, an ImportError is raised at the point of first access. Developers can also control lazy import behavior globally using the PYTHON_LAZY_IMPORTS environment variable or the -X lazy_imports command-line option.

PEP 814: New Built-in Type frozendict

Python 3.15 introduces frozendict, a new immutable built-in mapping type. It inherits directly from object rather than subclassing dict. As long as all keys and values are hashable, the frozendict itself is hashable. A frozendict preserves insertion order, although order is ignored during equality comparisons.

The addition of frozendict greatly simplifies working with immutable mappings—especially when using mappings as dictionary keys or set elements.

>>> a = frozendict(x=1, y=2)
>>> a
frozendict({'x': 1, 'y': 2})
>>> a['z'] = 3
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    a['z'] = 3
    ~^^^^^
TypeError: 'frozendict' object does not support item assignment
>>> b = frozendict(y=2, x=1)
>>> hash(a) == hash(b)
True
>>> a == b
True

PEP 798: Unpacking in Comprehensions

List, set, and dictionary comprehensions, as well as generator expressions, now support unpacking using * and **. This naturally extends the unpacking syntax introduced in PEP 448, making it simple to flatten multiple iterables or merge multiple dictionaries into a single structure as a direct alternative to nested comprehensions or itertools.chain().

>>> lists = [[1, 2], [3, 4], [5]]
>>> [*L for L in lists]  # Equivalent to [x for L in lists for x in L]
[1, 2, 3, 4, 5]

>>> sets = [{1, 2}, {2, 3}, {3, 4}]
>>> {*s for s in sets}  # Equivalent to {x for s in sets for x in s}
{1, 2, 3, 4}

>>> dicts = [{'a': 1}, {'b': 2}, {'a': 3}]
>>> {**d for d in dicts}  # Equivalent to {k: v for d in dicts for k,v in d.items()}
{'a': 3, 'b': 2}

Generator expressions work in the exact same manner:

>>> gen = (*L for L in lists)  # Equivalent to (x for L in lists for x in L)
>>> list(gen)
[1, 2, 3, 4, 5]

PEP 661: New Built-in Type sentinel

Python 3.15 adds the sentinel type to the builtins module, offering a standardized way to define unique sentinel values with clean string representations. Sentinel objects maintain identity across shallow copies, support the | union operator in type expressions, and can be pickled when importable by module and attribute name.

PEP 803: Stable ABI for Free-Threaded Builds (abi3t)

For free-threaded Python builds, C extensions can now target a dedicated stable ABI designated as abi3t. Opting into abi3t requires adopting modern APIs such as those defined in PEP 697 and utilizing the new module export hook PyModExport_* introduced in PEP 793.

Extensions unable to adopt abi3t immediately should continue to provide dual builds for the traditional stable ABI (abi3) and the version-specific free-threaded ABI (cp315t).

Upgrade Recommendations and Considerations

Recommendation DimensionDetails
Who Should UpgradeDevelopers of large-scale applications with demanding startup time requirements; developers handling extensive dictionary merging operations; library maintainers actively adopting free-threaded C extensions.
When to UpgradeImmediately explore Python 3.15 in local development and testing environments. For production environments, it is recommended to wait until dependencies (especially libraries with C extensions) have adopted abi3t or cp315t before migrating.
Important Considerations• Tracing features in cProfile have been consolidated into the new profiling module (PEP 799).
• The legacy profile module is deprecated and will be removed in Python 3.17.
• import statements inside .pth files are silently deprecated in favor of new .start configuration files (PEP 829).
• The PyGILState API family has been soft-deprecated, with no new additions planned (PEP 788).

References