NewsProgramming LanguagesPython

Python 3.15: Lazy Imports, frozendict, and a Faster JIT

Python 3.15 featured image showing Python logo with lazy imports and frozendict code blocks

Python 3.15 shipped on October 9. The headline feature is explicit lazy imports — a syntax change that defers module loading until first use. Meta’s codebase saw a 70% reduction in initialization time after adopting the pattern. The release also ships frozendict as a built-in type, a proper sentinel, unpacking in comprehensions, and a JIT compiler that is finally showing numbers worth quoting. Here is what matters and what to watch.

Lazy Imports: The Startup Fix Python Needed

For years, heavy Python apps had a startup problem. A web server importing Django, SQLAlchemy, and a handful of ML libraries could spend 400–800ms doing nothing but running import statements before accepting a single request. The standard fix was manual workaround: move imports inside functions, use importlib tricks, or fork CPython like Meta did. Python 3.15 makes this a first-class feature.

The new lazy soft keyword defers module loading until the imported name is first accessed:

lazy import json
lazy from pathlib import Path

# At this point, nothing is loaded — proxy objects only
# The real import fires on first use:
data = json.loads('{"key": "value"}')

For projects that need to stay compatible with older Python versions, there is a backwards-compatible approach using the __lazy_modules__ list. On Python 3.15+, those imports become lazy. On older versions, the list is ignored and imports proceed normally.

__lazy_modules__ = ['numpy', 'pandas', 'heavy_analytics_lib']
import numpy
import pandas

The practical wins are real. CLI tools are the clearest example — when a user runs --help, there is no reason to import a plotting library or a database driver. Meta’s 70% initialization reduction was not a lab benchmark; it came from converting production libraries with deep dependency graphs.

Two risks worth naming. First: late error detection. An ImportError that used to surface at startup will now surface when you first access the lazy import, potentially during request handling or a background job. Second: thread safety. Startup was previously single-threaded; now any thread that first touches a lazy import triggers the load. Neither is a dealbreaker, but both require awareness when adopting this pattern in concurrent systems.

frozendict: A Built-in That Took 20 Years

Python developers have needed an immutable, hashable dictionary for a long time. The standard library offered types.MappingProxyType as a partial answer, but it was a view over a mutable dict — not genuinely hashable, not usable as a dict key. Third-party packages filled the gap. Python 3.15 ships frozendict as a proper built-in via PEP 814.

import functools

config = frozendict({"model": "gpt-4o", "temperature": 0.7})

# Use as a cache key
@functools.lru_cache
def run_query(cfg: frozendict) -> str:
    ...

# Use as a set member or dict key
seen_configs = {frozendict(user="alice"), frozendict(user="bob")}
lookup = {frozendict(scope="read"): handler_fn}

A few things to know before adopting it. frozendict is not a dict subclass — isinstance(fd, dict) returns False. That is intentional: it avoids inheriting mutation methods that would need to raise errors. The immutability is also shallow: a nested list inside a frozendict is still mutable, and a frozendict containing a list cannot be hashed. These are the same constraints that apply to frozenset, so the behavior is at least consistent.

The JIT Keeps Compounding

Python’s experimental JIT has been technically available since 3.13. It has not been worth enabling until now. Python 3.15 shows 7–8% geometric mean improvement on x86-64 Linux and 11–12% on AArch64 macOS, with upgrades to LLVM 21, a new tracing frontend, basic register allocation, and lower memory usage for generated machine code.

The trajectory matters more than any single number: roughly 2% in 3.13, 4–5% in 3.14, 7–8% in 3.15. The JIT is still experimental and opt-in — it will not activate automatically. It is also not PyPy. But 8% on a distributed workload has real infrastructure cost implications, and the trend line is clearly heading somewhere.

Smaller Wins Worth Knowing

Python 3.15 also ships sentinel as a built-in. The old _MISSING = object() pattern works fine but produces ugly reprs and does not pickle cleanly. sentinel("MISSING") gives you a named, picklable, identity-preserving sentinel that works properly with type annotations.

PEP 798 adds unpacking to comprehensions:

# Before Python 3.15
flat = [x for sublist in lists for x in sublist]

# Python 3.15
flat = [*sublist for sublist in lists]

# Merge dicts in one comprehension
merged = {**d for d in dict_list}

What Actually Breaks

UTF-8 is now the default encoding on all platforms. Code that calls open() without an explicit encoding= argument and relies on locale defaults — especially on Windows — may produce different output. The fix is to pass encoding="utf-8" explicitly.

Several long-deprecated APIs are now removed: datetime.utcnow() (use datetime.now(datetime.UTC)), importlib.resources.read_text(), and the internal sre_compile, sre_constants, sre_parse modules. The regex internals are the most likely surprise — some packages accessed them directly. Check your dependencies before upgrading. The official What’s New page has the full list.

Should You Upgrade?

For greenfield projects: yes. The new built-ins are cleaner than their alternatives, and lazy imports are worth adopting for anything with non-trivial startup cost. For existing projects: check your dependencies first. The UTF-8 default and removed APIs are the likeliest sources of breakage, and most projects will need small fixes rather than rewrites.

Python 3.15 is not a headline release — there is no walrus operator moment, no match statement. What it is: a release that fixes chronic annoyances that Python developers have been patching around for years. That is often more valuable than a flashy syntax addition. The official download is available now.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *

    More in:News