In a long-awaited move for functional programming enthusiasts and library authors, the Python Steering Council has officially accepted PEP 814. This PEP...

What PEP 814 Brings to the Language

The Python Steering Council has accepted PEP 814, which introduces frozendict as a built-in immutable mapping type. For functional-programming enthusiasts and library authors, that fills a long-standing gap: lists have tuple, sets have frozenset, and mappings finally get a first-class immutable counterpart. In Python 3.15, you can build hashable, nested-safe configuration and intermediate data without rolling a custom type or relying on a third-party package for the same idea.

A frozendict behaves like a read-only dict for lookup, iteration, and membership tests. Once constructed, keys and values cannot be added, removed, or reassigned through the public API. That immutability is what makes the type usable as a dict key or set member when the values it holds are themselves hashable—something a plain dict can never do.

Why Immutability Matters in Real Code

Mutable dicts are convenient defaults, but they make sharing state risky. A helper that receives a config mapping and mutates it can surprise callers several stack frames away. Passing a frozendict documents intent: this structure is data, not a scratchpad. Callers keep a stable snapshot; callees must build a new mapping if they need different contents.

Library authors benefit in two common patterns. First, public APIs can return frozendicts so consumers cannot accidentally corrupt internal caches or registries. Second, default arguments that should be constant—such as empty option maps—can use a frozendict instead of the classic mutable-default trap with None and a local empty dict. Functional style also gets easier: pure functions that take mappings and return new ones compose cleanly when inputs cannot change underfoot.

  • Use frozendict for configuration, feature flags, and protocol metadata that should not change after load.
  • Prefer it for nested structures that must be hashable end-to-end (with hashable values only).
  • Keep ordinary dict for builders, accumulators, and any code that legitimately grows or rewrites keys over time.

Practical Tradeoffs and How to Adopt It

Immutability is not free. Building a frozendict means copying or freezing at construction time; hot loops that repeatedly “update” by creating new instances may pay more allocation cost than in-place dict edits. Measure where it matters, and treat frozendict as a boundary type—freeze at API edges and inside caches, mutate freely while assembling data. Converting at the last moment keeps intermediate work cheap and published state safe.

Values inside a frozendict are not automatically frozen. A frozendict of lists is still mutable through those lists. For true deep immutability, nest frozendicts, tuples, and frozensets, or freeze recursively at construction. When migrating libraries, expose frozendict in return types first, then accept either mapping type in parameters so existing callers keep working. Document that equality and hashing follow mapping semantics, and that only fully hashable contents yield a hashable frozendict—same mental model as frozenset of unhashables, but for key–value pairs.

Where It Fits in Day-to-Day Python

Think of frozendict as the missing twin of the other immutable containers, not a replacement for every dict. Use it when you need hashability, safe sharing across threads or async tasks without defensive copies, or clear contracts in public APIs. Keep mutable dicts for parsers, builders, and local accumulation. With PEP 814 accepted for Python 3.15, that choice becomes part of the standard library rather than a project-specific convention—and functional and library-oriented code can rely on the same primitive everywhere.

Automate Your Content with AI Video Generator

Try it Free →