Skip to main content

python-frozendict

A back-port of Python 3.15's frozendict builtin for older Python versions.

On Python 3.15+, you can write:

SETTINGS = frozendict({"debug": True, "level": 3})

This package provides the same behaviour on every supported Python version (3.8+) via a small, focused, dependency-free implementation.

Why?

A frozendict is an immutable, hashable mapping. It is useful wherever you would reach for dict but want a value that:

  • cannot be mutated by accident (or on purpose);
  • is hashable and therefore usable as a dict key or a set member;
  • serves as a clear, self-documenting signal that the data is fixed.
from frozendict import frozendict

USER_ROLES = frozendict({
    "alice": "admin",
    "bob": "editor",
})

# `USER_ROLES` is a valid dict key, set member, function default, etc.
cache = {USER_ROLES: "loaded"}

# Attempting to mutate it raises `TypeError`:
USER_ROLES["carol"] = "viewer"
# -> TypeError: 'frozendict' object does not support item assignment

Installation

pip install python-frozendict

Usage

Basic construction

from frozendict import frozendict

empty = frozendict()
from_dict = frozendict({"a": 1, "b": 2})
from_pairs = frozendict([("a", 1), ("b", 2)])
from_mapping = frozendict(some_Mapping_instance)
copy_of = frozendict(from_dict)  # new object, same content

The constructor accepts at most one positional argument (a dict, a Mapping, another frozendict, or any iterable of pairs). Keyword arguments are rejected, mirroring dict itself.

Reading

frozendict supports the full read-only mapping interface:

fd = frozendict({"a": 1, "b": 2, "c": 3})

fd["a"]              # 1
len(fd)              # 3
"a" in fd            # True
list(fd)             # ["a", "b", "c"]
list(fd.keys())      # ["a", "b", "c"]
list(fd.values())    # [1, 2, 3]
list(fd.items())     # [("a", 1), ("b", 2), ("c", 3)]
fd.get("z", 0)       # 0
list(reversed(fd))   # ["c", "b", "a"]

It is registered as a collections.abc.Mapping so it works with any function that accepts a generic mapping.

Hashing and equality

frozendict is hashable, if all of its values are hashable. Two frozendict objects with the same content compare equal and have the same hash, independent of insertion order:

a = frozendict({"a": 1, "b": 2})
b = frozendict({"b": 2, "a": 1})

a == b     # True
hash(a) == hash(b)   # True

A frozendict compares equal to a dict with the same content, and is usable as a dict key or a set element:

fd = frozendict({"a": 1})
{fd: "value"}            # {frozendict({'a': 1}): "value"}
{fd, frozendict({"a": 1})}   # a single-element set: {frozendict({'a': 1})}

If any value is unhashable, calling hash(fd) raises TypeError — this mirrors the behaviour of tuple.

Merging with |

The | operator returns a new frozendict; the operands are not mutated:

a = frozendict({"a": 1, "b": 2})
b = frozendict({"b": 99, "c": 3})

a | b           # frozendict({'a': 1, 'b': 99, 'c': 3})
{"x": 0} | a    # frozendict({'x': 0, 'a': 1, 'b': 2})

On key conflicts, the right-hand side wins — same as the built-in dict merge.

Immutability

Any attempt to mutate a frozendict raises TypeError:

fd = frozendict({"a": 1})
fd["b"] = 2        # TypeError
del fd["a"]        # TypeError
fd.clear()         # TypeError
fd.update({"b": 2})  # TypeError
fd.setdefault("b", 2)  # TypeError
fd.pop("a")        # TypeError
fd.popitem()       # TypeError

Copy and pickle

copy.copy(fd), copy.deepcopy(fd), and fd.copy() all return the same object (there is no need to copy an immutable value).

Pickling and unpickling preserves content but not identity:

import pickle
fd = frozendict({"a": 1, "b": 2})
restored = pickle.loads(pickle.dumps(fd))
restored == fd          # True
restored is fd          # False

Subclassing

Subclassing is not supported. Both class Sub(frozendict): pass and type("Sub", (frozendict,), {}) raise TypeError.

API

frozendict(*[mapping_or_pairs]*)

Construct a new frozendict. At most one positional argument is accepted:

Argument Behaviour
omitted empty frozendict
dict shallow copy of the dict
frozendict shallow copy
collections.abc.Mapping shallow copy via dict(mapping)
iterable of (key, value) equivalent to dict(iterable)

Keyword arguments are rejected. Passing more than one positional argument raises TypeError.

Methods and operators

Member Description
fd[key] raise KeyError if missing
key in fd membership test
len(fd) number of items
iter(fd) iterate over keys (insertion order)
reversed(fd) iterate over keys in reverse
fd.keys() / fd.values() / fd.items() mapping views (read-only)
fd.get(key, default=None) safe lookup
fd.copy() returns fd (immutable)
fd == other content-based equality
hash(fd) content-based hash (cached)
fd | other / other | fd merge, returns a new frozendict
repr(fd) / str(fd) "frozendict({...})"
bool(fd) False for empty, True otherwise
pickle.dumps/loads(fd) content preserved
copy.copy(fd) / copy.deepcopy(fd) both return fd
dict(fd) convert to dict

Forbidden operations

These all raise TypeError:

Attempted Error message
fd[key] = value 'frozendict' object does not support item assignment
del fd[key] 'frozendict' object does not support item deletion
fd.clear() 'frozendict' object does not support item assignment
fd.pop(...) 'frozendict' object does not support item deletion
fd.popitem() 'frozendict' object does not support item deletion
fd.setdefault(...) 'frozendict' object does not support item assignment
fd.update(...) 'frozendict' object does not support item assignment
class Sub(fd): subclassing frozendict is not supported
fd.foo = "bar" 'frozendict' object has no attribute 'foo'

Python 3.15+ native syntax

When you are running on Python 3.15+, frozendict is a true builtin. The package's own API still works identically, so you can use either:

# Native (Python 3.15+ only):
SETTINGS = frozendict({"debug": True})

# Cross-version equivalent via this package:
from frozendict import frozendict
SETTINGS = frozendict({"debug": True})

Compatibility

  • Python 3.8 through 3.15+ (uses collections.abc.Mapping; on 3.9+ uses modern union / merge syntax in the docs only).
  • No third-party dependencies — uses only the standard library.

Running the tests

python -m unittest test_frozendict.py -v

Retirement

This project will reach its end-of-life around October 1, 2031 — the official EOL date of Python 3.15 — and the exact timeline could be slightly delayed. We plan to ship the final stable release in November 2031. After this release, all support will cease and the repository will be officially archived, as this library is developed solely to bring frozendict compatibility to Python 3.15 and older versions.

License

MIT

Metadata

Release files for python-frozendict 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for python-frozendict 0.1.0
File Size Uploaded Provenance
python_frozendict-0.1.0.tar.gz 14.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for python-frozendict 0.1.0
File Interpreter ABI Platform
python_frozendict-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.9 kB

Release files / python_frozendict-0.1.0.tar.gz

Download URL python_frozendict-0.1.0.tar.gz
Size 14.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b78395973abb05431b9333a651d0a02a71e500ec7546070e188f353c9c6f2103
BLAKE2b-256 checksum
How to use checksums
e0c46357cef668a77399fda43def63b22268a146b1be31ff06246c3d72170ecd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release files / python_frozendict-0.1.0-py3-none-any.whl

Download URL python_frozendict-0.1.0-py3-none-any.whl
Size 8.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
08aa62fae8fc2606014f775a4af3d1094ddae7aefe747c169e8c40e259acc9eb
BLAKE2b-256 checksum
How to use checksums
bb38ece18ac2625730152e060d0e8080991a7cd378fa13d69902c54076d58317
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Security for python-frozendict 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page