classification
Title: Dictionaries should support __add__
Type: enhancement Stage:
Components: Interpreter Core Versions: Python 3.2, Python 2.7
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: QuantumTim, alexandre.vassalotti, benjamin.peterson, exarkun, ezio.melotti, hotdog003, loewis, rhettinger, terry.reedy
Priority: low Keywords:

Created on 2009-07-03 20:33 by hotdog003, last changed 2009-07-10 21:01 by terry.reedy. This issue is now closed.

Messages (16)
msg90074 - (view) Author: Michael W. (hotdog003) Date: 2009-07-03 20:33
Summary:
Dictionaries should support being added to other dictionaries instead of
using update(). This should be a relatively easy fix and would make the
language more pythonic.

How to reproduce:
$ python
Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) 
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> {1: 2, 3: 4} + {5: 6, 7: 8}

What happens:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

What should happen:
{1: 2, 3: 4, 5: 6, 7: 8}

Temporary workaround:
>>> a = {1: 2, 3: 4}
>>> b = {5: 6, 7: 8}
>>> c = a.copy()
>>> c.update(b)
>>> print c
{1: 2, 3: 4, 5: 6, 7: 8}
This is undesirable because it is not very compact and hard to read. Why
should any language take five lines of code to merge only two dictionaries?
msg90075 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2009-07-03 21:14
What would you do in case of duplicate keys?
msg90081 - (view) Author: Alexandre Vassalotti (alexandre.vassalotti) * (Python committer) Date: 2009-07-03 21:36
I am against adding __add__ to dict, since merging dictionaries is not a
commutative operation.

If a short syntax is desired for merging dictionaries, the just define a
function. For example: 

def merge_dicts(*args):
  result = {}
  for x in args:
    result.update(x)
  return result
msg90132 - (view) Author: Tim Gordon (QuantumTim) Date: 2009-07-04 22:34
__add__ is non-commutative for lists, tuples, strings etc. - perhaps 
non-commutative wasn't quite what you were looking for :p.
msg90133 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2009-07-04 22:39
Why so much opposition to the shorter spelling of .copy() & .update()? 
As Tim pointed out, lists, tuples, and strings all provide this
shortcut.  Why not dicts?
msg90135 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2009-07-04 23:27
Lists, tuples, and strings are all sequences. Adding two non-ordering
mappings makes much less sense in my head than two sequences.
msg90136 - (view) Author: Alexandre Vassalotti (alexandre.vassalotti) * (Python committer) Date: 2009-07-04 23:37
Tim Gordon wrote:
> __add__ is non-commutative for lists, tuples, strings etc. - perhaps 
> non-commutative wasn't quite what you were looking for :p.

Yeah, I was not clear in my explanation.

The thing is for lists, tuples, string and other ordered types
concatenation is a well-defined concept. Whereas for dictionaries is not
obvious what concatenation should do with duplicate keys. For example,
what would be the result of {"a": 1, "b": 2} + {"a": 2, "b": 1}? Should
it be {"a": 1, "b": 2} or {"a": 2, "b": 1}?

Also, it would be inconsistent the use of | for the union operation of sets.
msg90137 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2009-07-04 23:51
A dict.merge() method could be added so that:

>>> a = dict(x=10, y=20)
>>> b = dict(y=30, z=40)
>>> a.merge(b)
dict(x=10, y=20, z=40)
>>> b.merge(a)
dict(y=30, z=40, x=10)

In case of duplicate keys, the items of the second dict with the same
keys will be discarded (even if dict.update() does the opposite, I think
here it make more sense in this way).

I never needed to do something like this though, so I'm +0 about it.
msg90138 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-07-05 00:23
IIRC, Guido has previously rejected this suggestion and its variants.
msg90140 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2009-07-05 03:26
> IIRC, Guido has previously rejected this suggestion and its variants.

Got a link?  It'd be nice to know what the rationale was.
msg90141 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2009-07-05 03:41
Regardless of whether this feature will be dict.merge or __add__, it
needs to work its way through python-ideas first.
msg90193 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-07-06 21:23
FWIW, I'm -1 on the proposal because it partially overlaps the existing
capability of dict.update().  To the extent it doesn't overlap, it is
use case challenged (typically, it doesn't make sense to build a
brand-new dictionary from two independent dictionaries and the atypical
case easily fulfilled by a couple of updates on an empty dict).  

Also, the notation itself is at odds with the existing pipe-operator
used by sets and by dict views.

FWIW, there are other alternatives to directly combining dictionaries. 
See http://code.activestate.com/recipes/305268/ for one example.
msg90194 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2009-07-06 21:47
FWIW, here are some use cases:

http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/python/context.py#L32
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/python/log.py#L270
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/words/xish/utility.py#L23
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/web/microdom.py#L720
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/web/microdom.py#L738
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/test/test_adbapi.py#L370
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/test/test_zshcomp.py#L34
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/mail/scripts/mailmail.py#L345
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/internet/_dumbwin32proc.py#L167
http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/lore/default.py#L17
msg90202 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2009-07-06 23:59
ISTM these examples show how little value would come from fattening-up
the dict API.  The examples use the copy/update pattern which is clear,
explicit, and extendable to n-ary cases without incurring O(n**2)
behavior.  Tranforming them to a f=d+e pattern saves a few characters;
doesn't add any speed; makes it less clear that we're operating on
dictionaries; and does not extend well to the n-ary case (f=a+b+c+d+e
which copies a's elements five times, b's four times, etc.)  No new
functionality gets added -- all this is is a piece of syntactic sugar
that obscures what is going-on under the hood.  The dict API is one of
the most fundamental in the language; it needs to be kept as clean and
hazard-free as possible.
msg90203 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2009-07-07 00:00
Cool.  I'm convinced.
msg90397 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2009-07-10 21:01
> what would be the result of {"a": 1, "b": 2} + {"a": 2, "b": 1}?
> Should it be {"a": 1, "b": 2} or {"a": 2, "b": 1}?

or {"a":[1,2], "b":[1,2]}
As I remember, Guido rejected because of this ambiguity.
History
Date User Action Args
2009-07-10 21:01:32terry.reedysetnosy: + terry.reedy
messages: + msg90397
2009-07-07 00:00:27exarkunsetmessages: + msg90203
2009-07-06 23:59:22rhettingersetmessages: + msg90202
2009-07-06 21:47:04exarkunsetmessages: + msg90194
2009-07-06 21:23:52rhettingersetmessages: + msg90193
2009-07-05 03:41:29benjamin.petersonsetstatus: open -> closed
resolution: rejected
messages: + msg90141
2009-07-05 03:26:51exarkunsetmessages: + msg90140
2009-07-05 00:23:23rhettingersetnosy: + rhettinger
messages: + msg90138
2009-07-04 23:51:47ezio.melottisetstatus: closed -> open
priority: low

versions: + Python 2.7, Python 3.2, - Python 2.6
nosy: + ezio.melotti

messages: + msg90137
resolution: rejected -> (no value)
2009-07-04 23:38:03alexandre.vassalottisetstatus: open -> closed
2009-07-04 23:37:39alexandre.vassalottisetstatus: closed -> open

messages: + msg90136
2009-07-04 23:27:50benjamin.petersonsetstatus: open -> closed
nosy: + benjamin.peterson
messages: + msg90135

2009-07-04 22:39:58exarkunsetnosy: + exarkun
messages: + msg90133
2009-07-04 22:34:10QuantumTimsetstatus: pending -> open
nosy: + QuantumTim
messages: + msg90132

2009-07-03 21:37:00alexandre.vassalottisetstatus: open -> pending

nosy: + alexandre.vassalotti
messages: + msg90081

resolution: rejected
2009-07-03 21:14:02loewissetnosy: + loewis
messages: + msg90075
2009-07-03 20:33:57hotdog003create