========= frozndict
.. image:: https://raw.githubusercontent.com/wiseaidev/frozndict/main/assets/banner.png :target: https://github.com/wiseaidev/frozndict/ :alt: frozndict - Rust-Powered Immutable Dictionary :align: center
.. image:: https://img.shields.io/badge/License-MIT-blue.svg :target: https://github.com/wiseaidev/frozndict/blob/main/LICENSE :alt: License
.. image:: https://img.shields.io/pypi/v/frozndict.svg :target: https://pypi.org/project/frozndict/ :alt: PyPI version
.. image:: https://img.shields.io/github/repo-size/wiseaidev/frozndict :target: https://github.com/wiseaidev/frozndict/ :alt: Repo Size
.. image:: https://dl.circleci.com/status-badge/img/gh/wiseaidev/frozndict/tree/main.svg?style=svg&circle-token=CCIPRJ_86zVdw8bxskJfEWVcmN7d5_65a0e201b0e167e30852ad63c0c742c20c4e9dd0 :target: https://dl.circleci.com/status-badge/redirect/gh/wiseaidev/frozndict/tree/main :alt: CircleCI Build Status
frozndict is a production-grade, fully immutable Python dictionary powered by Rust and PyO3.
It is a drop-in companion to frozenset for dictionaries: hashable, thread-safe, insertion-ordered,
and engineered for performance.
๐ Performance Highlights
frozndict operates natively at the mathematical speed limits of the hardware, heavily outperforming its C counterparts and standard dictionary data structures on several fronts.
The following is a 1000-element dictionary micro-benchmark comparison in seconds (Smaller is better).
+-----------------------+--------------+-----------------+-----------------+-------------------+ | Operation (1000 int) | Python dict | immutables.Map | frozendict (C) | frozndict ๐ง | +=======================+==============+=================+=================+===================+ | Construction | 4.92e-06 ๐ | 1.61e-04 | 6.19e-06 | 5.79e-05 | +-----------------------+--------------+-----------------+-----------------+-------------------+ | Clone O(1) | 4.88e-06 | 9.87e-08 ๐ | 4.38e-07 | 1.29e-07 | +-----------------------+--------------+-----------------+-----------------+-------------------+ | Equality O(1) | 1.66e-05 | 2.52e-08 ๐ | 1.66e-05 | 3.24e-08 | +-----------------------+--------------+-----------------+-----------------+-------------------+ | Iteration | 1.15e-05 | 1.69e-05 | 1.15e-05 | 6.08e-06 ๐ | +-----------------------+--------------+-----------------+-----------------+-------------------+ | Copy O(1) | 4.88e-06 | 2.17e-04 | 6.58e-08 | 6.42e-08 ๐ | +-----------------------+--------------+-----------------+-----------------+-------------------+
-
๐ Fastest Iteration in Class via Lazy Caching Keys, values, and items are built lazily on first access via a Rust
OnceCell. This means construction is ultra-fast, and every subsequentkeys()/values()/items()/ iteration call returns an O(1) view object wrapping a sharedArc, no per-call allocation at all. -
Deterministic O(1) Pre-Computed Hashing The dict-level hash is XOR-combined over all
(k, v)pairs exactly once at construction via inline multiplicative mixing, skipping the slow allocation of intermediatePyTupleinstances entirely. -
O(1)
copy()/deepcopy()& Clones A sharedArc<FrozenDictInner>is re-used across the original, all copies, and mapping constructors (frozendict(existing_fd)). No data is ever duplicated. -
Instantaneous O(1)
__eq__short-circuit Self-equality (o == o) returns true instantly viaArc::ptr_eq. Equality between distinctFrozenDictinstances first compares the pre-computed hashes, detecting mismatches in O(1) before any scanning. -
๐ Smallest Memory Footprint in Class frozndict has the smallest memory footprint compared to other Python/C alternatives (occupying just 24 bytes of overhead for the main wrapper class). Entries sit in a single flat
Box<[(isize, K, V)]>allocation plus a compactBox<[(isize, u32)]>lookup index, eliminating load-factor slack completely. -
Infallible Rust-Level Immutability Mutation is blocked safely at the Rust binary level, completely bypassing Python descriptor tricks.
๐ ๏ธ Requirements
- Python 3.12+
- Rust 1.89+ (only when building from source)
- Maturin 1.14+ (only when building from source)
๐จ Installation
With pip::
python3 -m pip install frozndict
Build from source for development::
git clone https://github.com/wiseaidev/frozndict.git
cd frozndict
python3 -m venv .venv
source .venv/bin/activate
pip install maturin
maturin develop --features python
๐ธ Usage
.. code-block:: python3
from frozndict import frozendict
Empty immutable dictionary.
frozendict({}) frozendict({})
Insertion order is preserved.
frozendict(b=2, a=1, c=3) frozendict({'b': 2, 'a': 1, 'c': 3})
list(frozendict(b=2, a=1).keys()) ['b', 'a']
Set operations on keys.
frozendict(a=1, b=2).keys() & {"a", "x"} frozenset({'a'})
Set operations on items.
frozendict(a=1, b=2).items() - {("a", 1)} frozenset({('b', 2)})
Pickle round-trip.
import pickle d = frozendict(a=1, b=2) pickle.loads(pickle.dumps(d)) == d True
copy / deepcopy (O(1)).
import copy copy.copy(d) == d True
Mapping ABC.
import collections.abc isinstance(d, collections.abc.Mapping) True
Subclassing.
class MyFrozen(frozendict): ... def summary(self): return f"{len(self)} keys" MyFrozen(a=1, b=2).summary() '2 keys'
Generic subscript.
frozendict[str, int] frozndict.FrozenDict[str, int]
fromkeys constructor.
frozendict.fromkeys(["x", "y"], "5") frozendict({'x': '5', 'y': '5'})
Fully hashable and usable as dict keys or set members.
set([frozendict(a=1, b=2), frozendict(b=2, a=1)]) {frozendict({'a': 1, 'b': 2})}
Mutation raises TypeError at the Rust binary level.
d["x"] = 1 TypeError: 'frozendict' object does not support mutation
Memory footprint comparison: frozndict (Rust) wrapper matches pointer size (24 bytes)
import frozendict frozendict.c_ext False frozendict.version '2.4.7' from frozendict import frozendict from frozndict import frozendict as frozndict d = {'x': 3, 'y': 4, 'z': {'a': 0, 'b': [3,1,{4,1},[5,9]]}} classes = (dict, frozndict, frozendict) objects = [k(d, c=1) for k in classes] import sys
sys.getsizeof shows: dict (184B) vs frozndict Rust (24B) vs frozendict C (192B)
tuple(map(sys.getsizeof, objects)) (184, 24, 192)
๐ Running Benchmarks
::
$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install maturin frozendict immutables
$ maturin develop --release --features python
$ python3 benchmarks/benchmark.py
The benchmark compares frozndict (Rust), frozendict (C), dict, and immutables.Map across construction, lookup, iteration, copy, pickle, hash, and set/delete operations.
๐ Credits
Built with: python_ ยท PyO3_ ยท maturin_ ยท pytest_ ยท black_ ยท isort_ ยท flake8_ ยท precommit_
๐ Contribute
Please refer to the Guideline_ for contribution instructions.
๐ License
Released under the MIT License_.
.. _MIT License: https://opensource.org/licenses/MIT .. _frozendict: https://pypi.org/project/frozendict/ .. _Marco Sulla: https://github.com/Marco-Sulla .. _Guideline: https://github.com/wiseaidev/frozndict/blob/main/CONTRIBUTING.rst .. _PyO3: https://pyo3.rs/ .. _maturin: https://github.com/PyO3/maturin .. _python: https://www.python.org/ .. _pytest: https://docs.pytest.org/en/7.1.x/ .. _flake8: https://flake8.pycqa.org/en/latest/ .. _black: https://black.readthedocs.io/en/stable/ .. _isort: https://github.com/PyCQA/isort .. _precommit: https://pre-commit.com/