From 7961e7111749b6c114b0f3098570bdc37bdc1c18 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 13 Nov 2025 14:52:47 +0100 Subject: [PATCH 1/7] PEP 814: Minor tweaks --- peps/pep-0814.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index 36cfc0a7c85..b8327306194 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -6,6 +6,8 @@ Status: Draft Type: Standards Track Created: 12-Nov-2025 Python-Version: 3.15 +Post-History: `13-Nov-2025 `__ + Abstract ======== @@ -92,8 +94,8 @@ protocol, so all expected methods of iteration are supported:: Iterating on ``frozendict``, as on ``dict``, uses the insertion order. -Hashing -------- +Hashing and Comparison +---------------------- ``frozendict`` instances can be hashable just like tuple objects:: @@ -114,6 +116,11 @@ Equality test does not depend on the items' order either. Example:: >>> a == b True +It's possible to compare ``frozendict`` to ``dict``. Example:: + + >>> frozendict(x=1, y=2) == dict(x=1, y=2) + True + Typing ------ From 95b64ae37b219f2f43b9547da0ee598e9b97f307 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 13 Nov 2025 22:41:37 +0100 Subject: [PATCH 2/7] Add PyAnyDict_Check() --- peps/pep-0814.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index b8327306194..b1a7471fcfe 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -145,10 +145,12 @@ C API Add the following APIs: -* ``PyFrozenDict_Type`` -* ``PyFrozenDict_New(collection)`` function +* ``PyAnyDict_Check(op)`` macro +* ``PyAnyDict_CheckExact(op)`` macro * ``PyFrozenDict_Check()`` macro * ``PyFrozenDict_CheckExact()`` macro +* ``PyFrozenDict_New(collection)`` function +* ``PyFrozenDict_Type`` Even if ``frozendict`` is not a ``dict`` subclass, it can be used with ``PyDict_GetItemRef()`` and similar "PyDict_Get" functions. From 95a233be4af48814af07c674194aa36c0ebf81b6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Nov 2025 11:21:18 +0100 Subject: [PATCH 3/7] Union operators --- peps/pep-0814.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index b1a7471fcfe..7a2f05edaa5 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -122,6 +122,36 @@ It's possible to compare ``frozendict`` to ``dict``. Example:: True +Union operators +--------------- + +It's possible to join two ``frozendict``, or one ``frozendict`` with a +``dict``, with the merge (``|``) operator. Example:: + + >>> frozendict(x=1) | frozendict(y=1) + frozendict({'x': 1, 'y': 1}) + >>> frozendict(x=1) | dict(y=1) + frozendict({'x': 1, 'y': 1}) + +If some keys are in common, the values of the right operand are taken:: + + >>> frozendict(x=1, y=2) | frozendict(y=5) + frozendict({'x': 1, 'y': 5}) + +The update operator `|=` does not modify a ``frozendict`` in-place, but +creates a new ``frozendict``:: + + >>> d = frozendict(x=1) + >>> copy = d + >>> d |= frozendict(y=2) + >>> d + frozendict({'x': 1, 'y': 2}) + >>> copy # left unchanged + frozendict({'x': 1}) + +See also :pep:`584` "Add Union Operators To dict". + + Typing ------ From 0cdd0f744534809e1d2088ef45e210d507c6bb8d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Nov 2025 11:41:42 +0100 Subject: [PATCH 4/7] Update peps/pep-0814.rst Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- peps/pep-0814.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index 7a2f05edaa5..b17972dd1e4 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -138,7 +138,7 @@ If some keys are in common, the values of the right operand are taken:: >>> frozendict(x=1, y=2) | frozendict(y=5) frozendict({'x': 1, 'y': 5}) -The update operator `|=` does not modify a ``frozendict`` in-place, but +The update operator ``|=`` does not modify a ``frozendict`` in-place, but creates a new ``frozendict``:: >>> d = frozendict(x=1) From e5b7f89ccc2747e412adaee1aa2ef436ad788ce9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Nov 2025 11:44:58 +0100 Subject: [PATCH 5/7] Add more construction --- peps/pep-0814.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index b17972dd1e4..978f914eaa1 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -77,6 +77,9 @@ Construction - another ``frozendict``, - or an iterable of key/value tuples. +* ``frozendict(collection, **kwargs)`` combines the two previous + constructions. + The insertion order is preserved. From 1ac69e08c015e49c30d67fdb219e94a5a0560343 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Nov 2025 18:48:37 +0100 Subject: [PATCH 6/7] shallow/deep copy; mutable values; clarify PEP 603 --- peps/pep-0814.rst | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index 978f914eaa1..da0ac3f2f3f 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -80,6 +80,12 @@ Construction * ``frozendict(collection, **kwargs)`` combines the two previous constructions. +Keys must be hashable and so immutable, but values can be mutable. +Using immutable values creates a hashtable ``frozendict``. + +Creating a ``frozendict`` from a ``dict``, ``frozendict(dict)``, has a +complexity of *O*\ (*n*): items are copied (shallow copy). + The insertion order is preserved. @@ -155,6 +161,29 @@ creates a new ``frozendict``:: See also :pep:`584` "Add Union Operators To dict". +Copy +---- + +``frozencopy.copy()`` returns a shallow copy. In CPython, it simply +returns the same ``frozendict`` (new reference). + +Use ``copy.deepcopy()`` to get a deep copy. + +Example:: + + >>> import copy + >>> d = frozendict(x=[]) + >>> shallow_copy = d.copy() + >>> deep_copy = copy.deepcopy(d) + >>> d['x'].append(4) + >>> d + frozendict({'x': [4]}) + >>> shallow_copy # modified! + frozendict({'x': [4]}) + >>> deep_copy # unchanged + frozendict({'x': []}) + + Typing ------ @@ -311,12 +340,10 @@ Relationship to PEP 603 frozenmap * ``excluding(key)`` * ``union(mapping=None, **kw)`` -========== ============== ============== -Complexity ``frozenmap`` ``frozendict`` -========== ============== ============== -Lookup *O*\ (log *n*) *O*\ (1) -Copy *O*\ (1) *O*\ (*n*) -========== ============== ============== + These methods to mutate a ``frozenmap`` have a complexity of *O*\ (1). + +* A mapping lookup (``mapping[key]``) has a complexity of *O*\ (log *n*) + with ``frozenmap`` and a complexity of *O*\ (1) with ``frozendict``. Reference Implementation From ede4a7019e1c30483251c4cf0b3e3d27b110001b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 14 Nov 2025 18:57:17 +0100 Subject: [PATCH 7/7] rephrase --- peps/pep-0814.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index da0ac3f2f3f..6ca46195a05 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -134,7 +134,7 @@ It's possible to compare ``frozendict`` to ``dict``. Example:: Union operators --------------- -It's possible to join two ``frozendict``, or one ``frozendict`` with a +It's possible to join two ``frozendict``, or a ``frozendict`` with a ``dict``, with the merge (``|``) operator. Example:: >>> frozendict(x=1) | frozendict(y=1) @@ -172,16 +172,16 @@ Use ``copy.deepcopy()`` to get a deep copy. Example:: >>> import copy - >>> d = frozendict(x=[]) + >>> d = frozendict(mutable=[]) >>> shallow_copy = d.copy() >>> deep_copy = copy.deepcopy(d) - >>> d['x'].append(4) + >>> d['mutable'].append('modified') >>> d - frozendict({'x': [4]}) + frozendict({'mutable': ['modified']}) >>> shallow_copy # modified! - frozendict({'x': [4]}) + frozendict({'mutable': ['modified']}) >>> deep_copy # unchanged - frozendict({'x': []}) + frozendict({'mutable': []}) Typing