Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 814 – Add frozendict built-in type

Author:
Victor Stinner <vstinner at python.org>, Donghee Na <donghee.na at python.org>
Status:
Draft
Type:
Standards Track
Created:
12-Nov-2025
Python-Version:
3.15

Table of Contents

Abstract

A new public immutable type frozendict is added to the builtins module.

We expect frozendict to be safe by design, as it prevents any unintended modifications. This addition benefits not only CPython’s standard library, but also third-party maintainers who can take advantage of a reliable, immutable dictionary type.

Rationale

The proposed frozendict type:

  • implements the collections.abc.Mapping protocol,
  • supports pickling.

The following use cases illustrate why an immutable mapping is desirable:

  • Immutable mappings are hashable which allows their use as dictionary keys or set elements.
  • This hashable property permits functions decorated with @functools.lru_cache() to accept immutable mappings as arguments. Unlike an immutable mapping, passing a plain dict to such a function results in error.
  • Using an immutable mapping as a function parameter’s default value avoids the problem of mutable default values.
  • Immutable mappings can be used to safely share dictionaries across thread and asynchronous task boundaries. The immutability makes it easier to reason about threads and asynchronous tasks.

There are already third-party frozendict and frozenmap packages available on PyPI, proving that there is demand for immutable mappings.

Specification

A new public immutable type frozendict is added to the builtins module. It is not a dict subclass but inherits directly from object.

Construction

frozendict implements a dict-like construction API:

  • frozendict() creates a new empty immutable mapping.
  • frozendict(**kwargs) creates a mapping from **kwargs, e.g. frozendict(x=1, y=2).
  • frozendict(collection) creates a mapping from the passed collection object. The passed collection object can be:
    • a dict,
    • another frozendict,
    • or an iterable of key/value tuples.

The insertion order is preserved.

Iteration

As frozendict implements the standard collections.abc.Mapping protocol, so all expected methods of iteration are supported:

assert list(m.items()) == [('foo', 'bar')]
assert list(m.keys()) == ['foo']
assert list(m.values()) == ['bar']
assert list(m) == ['foo']

Iterating on frozendict, as on dict, uses the insertion order.

Hashing

frozendict instances can be hashable just like tuple objects:

hash(frozendict(foo='bar'))  # works
hash(frozendict(foo=['a', 'b', 'c']))  # error, list is not hashable

The hash value does not depend on the items’ order. It is computed on keys and values. Pseudo-code of hash(frozendict):

hash(frozenset(frozendict.items()))

Equality test does not depend on the items’ order either. Example:

>>> a = frozendict(x=1, y=2)
>>> b = frozendict(y=2, x=1)
>>> hash(a) == hash(b)
True
>>> a == b
True

Typing

It is possible to use the standard typing notation for frozendicts:

m: frozendict[str, int] = frozendict(x=1)

Representation

frozendict will not use a special syntax for its representation. The repr() of a frozendict instance looks like this:

>>> frozendict(x=1, y=2)
frozendict({'x': 1, 'y': 2})

C API

Add the following APIs:

  • PyFrozenDict_Type
  • PyFrozenDict_New(collection) function
  • PyFrozenDict_Check() macro
  • PyFrozenDict_CheckExact() macro

Even if frozendict is not a dict subclass, it can be used with PyDict_GetItemRef() and similar “PyDict_Get” functions.

Passing a frozendict to PyDict_SetItem() or PyDict_DelItem() fails with TypeError. PyDict_Check() on a frozendict is false.

Exposing the C API helps authors of C extensions supporting frozendict when they need to support thread-safe immutable containers. It will be important since PEP 779 (Criteria for supported status for free-threaded Python) was accepted, people need this for their migration.

Differences between dict and frozendict

  • dict has more methods than frozendict:
    • __delitem__(key)
    • __setitem__(key, value)
    • clear()
    • pop(key)
    • popitem()
    • setdefault(key, value)
    • update(*args, **kwargs)
  • A frozendict can be hashed with hash(frozendict) if all keys and values can be hashed.

Possible candidates for frozendict in the stdlib

We have identified several stdlib modules where adopting frozendict can enhance safety and prevent unintended modifications by design. We also believe that there are additional potential use cases beyond the ones listed below.

Note: it remains possible to bind again a variable to a new modified frozendict or a new mutable dict.

Python modules

Replace dict with frozendict in function results:

  • email.headerregistry: ParameterizedMIMEHeader.params() (replace MappingProxyType)
  • enum: EnumType.__members__() (replace MappingProxyType)

Replace dict with frozendict for constants:

  • _opcode_metadata: _specializations, _specialized_opmap, opmap
  • _pydatetime: specs (in _format_time())
  • _pydecimal: _condition_map
  • bdb: _MonitoringTracer.EVENT_CALLBACK_MAP
  • dataclasses: _hash_action
  • dis: deoptmap, COMPILER_FLAG_NAMES
  • functools: _convert
  • gettext: _binary_ops, _c2py_ops
  • imaplib: Commands, Mon2num
  • json.decoder: _CONSTANTS, BACKSLASH
  • json.encoder: ESCAPE_DCT
  • json.tool: _group_to_theme_color
  • locale: locale_encoding_alias, locale_alias, windows_locale
  • opcode: _cache_format, _inline_cache_entries
  • optparse: _builtin_cvt
  • platform: _ver_stages, _default_architecture
  • plistlib: _BINARY_FORMAT
  • ssl: _PROTOCOL_NAMES
  • stringprep: b3_exceptions
  • symtable: _scopes_value_to_name
  • tarfile: PAX_NUMBER_FIELDS, _NAMED_FILTERS
  • token: tok_name, EXACT_TOKEN_TYPES
  • tomllib._parser: BASIC_STR_ESCAPE_REPLACEMENTS
  • typing: _PROTO_ALLOWLIST

Extension modules

Replace dict with frozendict for constants:

  • errno: errorcode

Relationship to PEP 416 frozendict

Since 2012 (PEP 416), the Python ecosystem has evolved:

  • asyncio was added in 2014 (Python 3.4)
  • Free threading was added in 2024 (Python 3.13)
  • concurrent.interpreters was added in 2025 (Python 3.14)

There are now more use cases to share immutable mappings.

frozendict now preserves the insertion order, whereas PEP 416 frozendict was unordered (as PEP 603 frozenmap). frozendict relies on the dict implementation which preserves the insertion order since Python 3.6.

The first motivation to add frozendict was to implement a sandbox in Python. It’s no longer the case in this PEP.

types.MappingProxyType was added in 2012 (Python 3.3). This type is not hashable and it’s not possible to inherit from it. It’s also easy to retrieve the original dictionary which can be mutated, for example using gc.get_referents().

Relationship to PEP 603 frozenmap

collections.frozenmap has different properties than frozendict:

  • frozenmap items are unordered, whereas frozendict preserves the insertion order.
  • frozenmap has additional methods:
    • including(key, value)
    • excluding(key)
    • union(mapping=None, **kw)
Complexity frozenmap frozendict
Lookup O(log n) O(1)
Copy O(1) O(n)

Reference Implementation

  • The reference implementation is still a work-in-progress.
  • frozendict shares most of its code with the dict type.
  • Add PyFrozenDictObject which inherits from PyDictObject and has an additional ma_hash member.

Thread Safety

Once the frozendict is created, it is immutable and can be shared safely between threads without any synchronization.

Future Work

We are also going to make frozendict to be more efficient in terms of memory usage and performance compared to dict in future.

Rejected Ideas

Inherit from dict

If frozendict inherits from dict, it would become possible to call dict methods to mutate an immutable frozendict. For example, it would be possible to call dict.__setitem__(frozendict, key, value).

It may be possible to prevent modifying frozendict using dict methods, but that would require to explicitly exclude frozendict which can affect dict performance. Also, there is a higher risk of forgetting to exclude frozendict in some methods.

If frozendict does not inherit from dict, there is no such issue.

New syntax for frozendict literals

Various syntaxes have been proposed to write frozendict literals.

A new syntax can be added later if needed.

References

Acknowledgements

This PEP is based on prior work from Yury Selivanov (PEP 603).


Source: https://github.com/python/peps/blob/main/peps/pep-0814.rst

Last modified: 2025-11-13 09:36:56 GMT