TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

PEP 584 – Add + and – operators to the built-in dict class

164 pointsby Ivoahabout 6 years ago

18 comments

kbdabout 6 years ago
&gt; An alternative to the + operator is the pipe | operator, which is used for set union. This suggestion did not receive much support on Python-Ideas.<p>That&#x27;s disappointing. It&#x27;s always been on my Python wish list that dicts would subclass sets, as dicts are essentially sets with values attached. Pretty much everywhere you can use a set you can use a dict and it acts like the set of its keys. For example:<p><pre><code> &gt;&gt;&gt; s = {&#x27;a&#x27;,&#x27;b&#x27;,&#x27;c&#x27;} &gt;&gt;&gt; d = {i: i.upper() for i in s} &gt;&gt;&gt; list(d) == list(s) True </code></pre> Dictionaries have been moving in this more ergonomic direction for a while. Originally, to union two dictionaries you had to say:<p><pre><code> &gt;&gt;&gt; d2 = {&#x27;d&#x27;: &#x27;D&#x27;} &gt;&gt;&gt; &gt;&gt;&gt; d3 = d.copy() &gt;&gt;&gt; d3.update(d2) &gt;&gt;&gt; d3 {&#x27;a&#x27;: &#x27;A&#x27;, &#x27;b&#x27;: &#x27;B&#x27;, &#x27;c&#x27;: &#x27;C&#x27;, &#x27;d&#x27;: &#x27;D&#x27;} </code></pre> Nowadays, as the PEP points out, you can just say:<p><pre><code> &gt;&gt;&gt; {**d, **d2} {&#x27;a&#x27;: &#x27;A&#x27;, &#x27;b&#x27;: &#x27;B&#x27;, &#x27;c&#x27;: &#x27;C&#x27;, &#x27;d&#x27;: &#x27;D&#x27;} </code></pre> There&#x27;s no reason you shouldn&#x27;t have always been able to say d | d2, same as sets. Now I finally get my wish that dictionaries will behave more similarly to sets and they use the wrong set of operators.
评论 #19317985 未加载
评论 #19317654 未加载
评论 #19317519 未加载
评论 #19317916 未加载
评论 #19317475 未加载
评论 #19323519 未加载
评论 #19317494 未加载
评论 #19318267 未加载
评论 #19317470 未加载
评论 #19317530 未加载
zestypingabout 6 years ago
len(dict1 + dict2) does not equal len(dict1) + len(dict2) so using the + operator is nonsense.<p>The operators should be |, &amp;, and -, exactly as for sets, and the behaviour defined with just three rules:<p>1. The keys of dict1 [op] dict2 are the elements of dict1.keys() [op] dict2.keys().<p>2. The values of dict2 overwrite the values of dict1.<p>3. When either operand is a set, it is treated as a dict whose values are None.<p>This yields many useful operations and is simple to explain.<p>merge and update:<p><pre><code> {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2} | {&#x27;b&#x27;: 3, &#x27;c&#x27;: 4} =&gt; {&#x27;a&#x27;: 1, &#x27;b&#x27;: 3, &#x27;c&#x27;: 4} </code></pre> pick some items:<p><pre><code> {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2} &amp; {&#x27;b&#x27;: 3, &#x27;c&#x27;: 4} =&gt; {&#x27;b&#x27;: 3} </code></pre> remove some items:<p><pre><code> {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2} - {&#x27;b&#x27;: 3, &#x27;c&#x27;: 4} =&gt; {&#x27;a&#x27;: 1} </code></pre> reset values of some keys:<p><pre><code> {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2} | {&#x27;b&#x27;, &#x27;c&#x27;} =&gt; {&#x27;a&#x27;: 1, &#x27;b&#x27;: None, &#x27;c&#x27;: None} </code></pre> ensure all keys are present:<p><pre><code> {&#x27;b&#x27;, &#x27;c&#x27;} | {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2} =&gt; {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2, &#x27;c&#x27;: None} </code></pre> pick some items:<p><pre><code> {&#x27;b&#x27;, &#x27;c&#x27;} | {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2} =&gt; {&#x27;b&#x27;: 2} </code></pre> remove some items:<p><pre><code> {&#x27;a&#x27;: 1, &#x27;b&#x27;: 2} - {&#x27;b&#x27;, &#x27;c&#x27;} =&gt; {&#x27;a&#x27;: 1}</code></pre>
评论 #19318305 未加载
评论 #19321532 未加载
评论 #19318205 未加载
评论 #19317842 未加载
评论 #19318121 未加载
jerfabout 6 years ago
Unlike some of the other commenters, I&#x27;m fine with the + specification. + hasn&#x27;t been commutative in Python for a long time.<p>But the - bothers me, and nobody else seems to have mentioned this. {&quot;a&quot;: 1} - {&quot;a&quot;: 1} = {}, sure, but it is <i>way</i> less obvious to me that {&quot;a&quot;: 1} - {&quot;a&quot;: 2} = {}, and not {&quot;a&quot;: 1}. If you consider dictionaries as an unordered list of tuples (key, value) where keys happen to be unique and as a result of that you get nice O()-factors on access, that doesn&#x27;t make sense. You went to remove (&quot;a&quot;, 2), but saw (&quot;a&quot;, 1) and thought, &quot;eh, close enough&quot;. But it&#x27;s not the same thing.<p>If you think of a dict as a set that happens to have associated values, the specification makes more sense, but if you dig into that line of thought, that turns out to be a rather weird way of thinking of them. Values really shouldn&#x27;t be thought of as second-class citizens of a dict. If you are going to go this route though, {&quot;a&quot;: 1} - {&quot;a&quot;} = {} (where the right-hand side is a set) actually makes <i>more</i> sense, without the spurious value on the right-hand side.<p>I&#x27;d actually rather conceive of the - operation as a &quot;dict minus an iterable that will yield keys to remove&quot;. This has the advantage of recovering the original {&quot;a&quot;: 1} - {&quot;a&quot;: 2} = {} semantics that probably is what people want in practice, just via a different method. But locking the right-hand side to a dict makes it weird.
评论 #19319942 未加载
评论 #19320309 未加载
wodenokotoabout 6 years ago
&gt; Analogously with list addition, the operator version is more restrictive, and requires that both arguments are dicts, while the augmented assignment version allows anything the update method allows, such as iterables of key&#x2F;value pairs.<p><pre><code> &gt;&gt;&gt; d + [(&#x27;spam&#x27;, 999)] Traceback (most recent call last): ... TypeError: can only merge dict (not &quot;list&quot;) to dict &gt;&gt;&gt; d += [(&#x27;spam&#x27;, 999)] &gt;&gt;&gt; print(d) {&#x27;spam&#x27;: 999, &#x27;eggs&#x27;: 2, &#x27;cheese&#x27;: &#x27;cheddar&#x27;, &#x27;aardvark&#x27;: &#x27;Ethel&#x27;} </code></pre> While I get the &quot;Because this is what lists do&quot;-argument, I am still wondering why there is a difference in the types allowed for `+` and `+=`?
评论 #19317374 未加载
评论 #19321477 未加载
ameliusabout 6 years ago
For sets I can understand what + and - means: you can add or subtract the sets (not add or remove an element directly). This should be like lists, e.g.<p><pre><code> [10,20] + [30] </code></pre> But what + and - would mean in the case of dicts is obscure. Better to just use full method names imho.
评论 #19318233 未加载
评论 #19317370 未加载
comexabout 6 years ago
The + operator looks great – I&#x27;ve personally experienced the papercut this solves multiple times, where it would be most natural to have &quot;combine two dicts&quot; operator:<p><pre><code> return {&#x27;a&#x27;: &#x27;b&#x27;} + other_dict </code></pre> but instead I had to assign to a variable and mutate with .update(), which is much more verbose:<p><pre><code> x = {&#x27;a&#x27;: &#x27;b&#x27;} x.update(other_dict) return x </code></pre> <i>However</i>, I was working in Python 2; Python 3 has<p><pre><code> {&#x27;a&#x27;: &#x27;b&#x27;, **other_dict} </code></pre> and even<p><pre><code> {**one_dict, **other_dict} </code></pre> though the PEP mentions that the latter doesn&#x27;t work in all circumstances. Still, it will be nice to have a more general operator; I personally don&#x27;t really care whether it&#x27;s called + or |.<p>On the other hand, the - operator seems... strange, in that it only considers the keys of its right-hand argument, and ignores the values. Seems like a footgun.
pettersabout 6 years ago
I think overloading + so that a + b != b + a is problematic.<p>I know this is the case for strings and lists, but those cases are very well established.
评论 #19318487 未加载
评论 #19317980 未加载
评论 #19317900 未加载
评论 #19318238 未加载
mk89about 6 years ago
Reminds me of Scala Maps[0].<p>Edit: after reading more carefully,...<p>&gt; Analogously with list addition, the operator version is more restrictive, and requires that both arguments are dicts, while the augmented assignment version allows anything the update method allows, such as iterables of key&#x2F;value pairs.<p>But why? Consistency in API behavior is important, and as a user I don&#x27;t want to have to read that I can add lists of pairs only with assignments. I hope the draft gets fixed.<p>[0]: <a href="https:&#x2F;&#x2F;docs.scala-lang.org&#x2F;overviews&#x2F;collections&#x2F;maps.html" rel="nofollow">https:&#x2F;&#x2F;docs.scala-lang.org&#x2F;overviews&#x2F;collections&#x2F;maps.html</a>
评论 #19317505 未加载
评论 #19318198 未加载
speedplaneabout 6 years ago
Python continues to introduce more non-intuitive semantics that may be a small boon to the the expert class of programmers, but comes at the expense of ease of adoption for beginners. It started by making everything a generator, which are not very easy to master, and for which there were plenty of perfectly good substitutes (e.g., xrange, iteritems). And now you &quot;add&quot; sets of items (which you can&#x27;t do in math) and when the update function worked well.<p>Python 3 is such a sad mess.
评论 #19317995 未加载
评论 #19320755 未加载
评论 #19317539 未加载
评论 #19320356 未加载
评论 #19317429 未加载
评论 #19317440 未加载
rurbanabout 6 years ago
dict.merge(d, ...) and dict.diff(d, ...) are more expressive and have a cleaner semantic.<p>overloading arithmetic ops for string, list or dict ops might only look elegant at first sight, but discrimination needs to be done at runtime, slowing down the most important arithmetic ops, and do not help much the casual code reader. It also cannot be used in normal python code as older python will fail, only in special internal code.<p>normal method names can be provided by external modules, so they are backwards compatible and will find more widely adoption.
评论 #19318224 未加载
评论 #19317847 未加载
s17nabout 6 years ago
It&#x27;s weird to use + for a non-commutative operation, right?
评论 #19317455 未加载
评论 #19318651 未加载
评论 #19318679 未加载
js2about 6 years ago
&gt; The implementation will be in C. (The author of this PEP would like to make it known that he is not able to write the implementation.)<p>I hope this is for a reason other than the author being unfamiliar with C. Otherwise the author is cheating themselves, because adding functionality to an existing code base is probably my favorite motivator for learning a new language.
评论 #19325560 未加载
varelazabout 6 years ago
I don&#x27;t like the idea, because a + b should produce new list c without modification of both. Which is not memory optimal and cost of it is not obvious. Also dict is used a lot for sub classes and that could break a lot of existing functionality with potentially no benefit for most of the developers. I don&#x27;t think that merging is very common operation for dicts and even so it could be done with 1 or 2 update function calls, but that will be obvious in that case, while &#x27;+&#x27; in deeps of duck typing code is not. Also absence of &#x27;+&#x27; operation for dicts is kind of guard for type validation in case if someone passed dict instead of integer. Which is pretty common when you parse some JSON from client.
评论 #19318216 未加载
sametmaxabout 6 years ago
One of the long awaited features, rejected by Guido many times, and finally accepted. Maybe we&#x27;ll get list.get(), functools.partial() as a c written builtin, pathlib.Path() as a primitive or inline try&#x2F;except, one day.
评论 #19318188 未加载
mrocheabout 6 years ago
<p><pre><code> def __add__(self, other): if isinstance(other, dict): new = type(self)() # May be a subclass of dict. new.update(self) new.update(other) return new </code></pre> Is there something I’m missing? To me it would be cleaner and more memory&#x2F;time performant to just `self.update(other)` rather than having a third list instance at operation time. But that would really only apply if you have truly massive dicts.
评论 #19319266 未加载
projektfuabout 6 years ago
Is there a real-world demand for the dictionary difference operator or is it just being proposed for completeness? I&#x27;m racking my brain to think of reasons to use it that would be more expressive than simply giving a list of keys to delete.
Grue3about 6 years ago
I like this. The current kwargs syntax is <i>very</i> confusing since it behaves very differently from funcall kwargs syntax.
评论 #19317755 未加载
IceDaneabout 6 years ago
This is what you get when you try to implement general mathematical concepts in a language that is horrible at expressing them. What a clusterfuck python is going to be in a few years.