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.Mappingprotocol, - 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 plaindictto 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.
- a
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_TypePyFrozenDict_New(collection)functionPyFrozenDict_Check()macroPyFrozenDict_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
dicthas more methods thanfrozendict:__delitem__(key)__setitem__(key, value)clear()pop(key)popitem()setdefault(key, value)update(*args, **kwargs)
- A
frozendictcan be hashed withhash(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()(replaceMappingProxyType)enum:EnumType.__members__()(replaceMappingProxyType)
Replace dict with frozendict for constants:
_opcode_metadata:_specializations,_specialized_opmap,opmap_pydatetime:specs(in_format_time())_pydecimal:_condition_mapbdb:_MonitoringTracer.EVENT_CALLBACK_MAPdataclasses:_hash_actiondis:deoptmap,COMPILER_FLAG_NAMESfunctools:_convertgettext:_binary_ops,_c2py_opsimaplib:Commands,Mon2numjson.decoder:_CONSTANTS,BACKSLASHjson.encoder:ESCAPE_DCTjson.tool:_group_to_theme_colorlocale:locale_encoding_alias,locale_alias,windows_localeopcode:_cache_format,_inline_cache_entriesoptparse:_builtin_cvtplatform:_ver_stages,_default_architectureplistlib:_BINARY_FORMATssl:_PROTOCOL_NAMESstringprep:b3_exceptionssymtable:_scopes_value_to_nametarfile:PAX_NUMBER_FIELDS,_NAMED_FILTERStoken:tok_name,EXACT_TOKEN_TYPEStomllib._parser:BASIC_STR_ESCAPE_REPLACEMENTStyping:_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:
asynciowas added in 2014 (Python 3.4)- Free threading was added in 2024 (Python 3.13)
concurrent.interpreterswas 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:
frozenmapitems are unordered, whereasfrozendictpreserves the insertion order.frozenmaphas 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.
frozendictshares most of its code with thedicttype.- Add
PyFrozenDictObjectwhich inherits fromPyDictObjectand has an additionalma_hashmember.
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).
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.
Source: https://github.com/python/peps/blob/main/peps/pep-0814.rst
Last modified: 2025-11-13 09:36:56 GMT