Hello community
Following the previous discussion that I started, I thought and came to several conclusions - the desire to add the operators -
, -=
to the dictionary in Python.
I’m Looking for Sponsorship
This proposal is currently seeking a sponsor from among the Python core developers. If you think the proposed idea merits further consideration and development into a full PEP, please reach out. I am prepared to refine this proposal with more detailed specifications, rationales, and comprehensive use cases upon obtaining sponsorship.
Abstract
This PEP proposes the addition of two new operators, -
and -=
to Python’s dictionary type. This enhancement follows the introduction of union operators (|
and |=
) for dictionaries in PEP 584. The subtraction operator (-
) will enable straightforward and efficient removal of keys from a dictionary, producing a new dictionary as a result, while the in-place subtraction operator (-=
) will modify the original dictionary. These operators are proposed to work only between dictionaries initially, simplifying both implementation and conceptual overhead.
Motivation
Following the successful adoption of PEP 584, which introduced the |
and =|
operators for dictionaries, there exists a natural progression to enhance dictionary manipulations further by introducing subtraction operators -
and -=
. This addition will provide a clear, concise syntax for creating new dictionaries by removing specific keys, a common task currently handled by less direct methods that often involve loops or comprehensions. The proposed operators are intuitive and align with Python’s overarching design philosophy of readability and simplicity in common operations.
Additionally, in previous discussions, the symmetric difference operation (^
) for dictionaries was considered. However, it seems that the use case for such an operator was limited and not as broadly useful as subtraction operations, which are more commonly required in practice (from a quick search I found dozens of use cases for the substruction operators in open-source, some of those examples can be found below).
Rationale
The rationale for introducing -
and -=
for dictionaries is to provide dictionary users with intuitive and efficient tools for key removal, akin to those available for sets. This PEP takes the position that this behavior is simple, obvious, usually the behavior we want, and should be the default behavior for dicts.
From first look it seems that the -
would be more useful than any of the other possible operations.
Note: it is debatable if we should expend all the set operations to dicts. This is out of focus for the PEP that I wish to propose, and for the sake of consistency and conciseness will not be included in this PEP.
Specification
The proposed syntax and behavior for the new operators are as follows:
-
d1 - d2
: Returns a new dictionary that includes only the keys fromd1
that are not present ind2
. The values in the resulting dictionary are fromd1
.
I.e.d1 - d2 = {k : v for k in d1 if k not in d2}
, Example:d1 = {"a": 1, "b": 2, "c": 3} d2 = {"b": 4} d3 = d1 - d2 # d3 is {"a": 1, "c": 3}
-
d1 -= d2
: Modifiesd1
in place, removing keys that appear ind2
.Example:d1 = {"a": 1, "b": 2, "c": 3} d2 = {"b": 4} d1 -= d2 # d1 is now {"a": 1, "c": 3}
While there are compelling use cases for extending these operations to allow d1 - set1
, this proposal initially limits the operations to dictionary operands to maintain focus and simplicity. Future expansions could explore interactions between dictionaries and sets, as well as further extensions to the |
operator introduced in PEP 584.
Code Examples and Use Cases
All the code examples here are from packages installed on my computer under the python3.10/site-packages
.
numpy/distutils/ccompiler_opt.py
Before:
feature.update({
k:v for k,v in cfeature.items() if k not in feature
})
After:
feature |= cfeature - feature
matplotlib/axes/_axes.py
Before:
return self.plot(
*args, **{k: v for k, v in kwargs.items() if k not in d})
After:
return self.plot(*args, **(kwargs - d))
sqlalchemy/cyextension/collections.pyx
Before:
else:
other = {cy_id(obj): obj for obj in iterable}
result._members = {k: v for k, v in self._members.items() if k not in other}
After:
else:
other = {cy_id(obj): obj for obj in iterable}
result._members = self._members - other
mypy/solve.py
Before:
originals.update({v.id: v for v in c.extra_tvars if v.id not in originals})
After:
originals |= {v.id: v for v in c.extra_tvars} - originals
setuptools/dist.py
Before:
metadata_only = set(self._DISTUTILS_UNSUPPORTED_METADATA)
metadata_only -= {"install_requires", "extras_require"}
dist_attrs = {k: v for k, v in attrs.items() if k not in metadata_only}
After (this one is an example of dict - set
operation that is a debatable and not included in the PEP that I want to propose):
metadata_only = set(self._DISTUTILS_UNSUPPORTED_METADATA)
metadata_only -= {"install_requires", "extras_require"}
dist_attrs = attrs - metadata_only
References
- PEP 584 – Add Union Operators To dict | peps.python.org
- Add Symmetric Difference Operators To Dict
- Dictionaries should have key-based set operations
Notes
- To be consistent with the
|=
existing operator, we should consider include adict.difference
method. I’m not sure if we should do so and I think that is a good point for discussion.
Thanks to everyone in advance!