[Python-ideas] Adding "+" and "+=" operators to dict

Steven D'Aprano steve at pearwood.info
Fri Feb 13 03:24:05 CET 2015


On Fri, Feb 13, 2015 at 12:06:44AM +0000, Andrew Barnert wrote:
> On Thursday, February 12, 2015 1:33 PM, Nathan Schneider <neatnate at gmail.com> wrote:
> 
> 
> > A reminder about PEP 448: Additional Unpacking Generalizations 
> > (https://www.python.org/dev/peps/pep-0448/), which claims that "it 
> > vastly simplifies types of 'addition' such as combining
> > dictionaries, and does so in an unambiguous and well-defined way". 
> > The spelling for combining two dicts would be: {**d1, **d2}

Very nice! That should be extensible to multiple arguments without the 
pathologically slow performance of repeated addition:

{**d1, **d2, **d3, **d4}

and you can select which dict you want to win in the event of clashes 
by changing the order.


> I like that this makes all of the bikeshedding questions obvious: it's 
> a dict display, so the result is clearly a dict rather than a 
> type(d1), it's clearly going to follow the same ordering rules for 
> duplicate keys as a dict display (d2 beats d1), and so on.
> 
> And it's nice that it's just a special case of a more general 
> improvement.
> 
> However, it is more verbose (and more full of symbols), and it doesn't 
> give you an obvious way to, say, merge two OrderedDicts into an 
> OrderedDict.

Here's a more general proposal. The dict constructor currently takes 
either a single mapping or a single iterable of (key,value) pairs. The 
update method takes a single mapping, or a single iterable of (k,v) 
pairs, AND any arbitrary keyword arguments.

We should generalise both of these to take *one or more* mappings and/or 
iterables, and solve the most common forms of copy-and-update. That 
avoids the (likely) pathologically slow behaviour of repeated addition, 
avoids any confusion over operators and having += duplicating the 
update method.

Then, merging in place uses:

d1.update(d2, d3, d4, d5)

and copy-and-merge uses:

dict(d1, d2, d3, d4, d5)  # like d1+d2+d3+d4+d5

where the d's can be any mapping, and you can optionally include keyword 
arguments as well.

You don't have to use dict, you can use any Mapping which uses the same 
constructor semantics. That means subclasses of dict ought to work 
(unless the subclass does something silly) and even OrderedDict ought to 
work (modulo the fact that regular dicts and keyword args are 
unordered).

Here's a proof of concept:

class Dict(dict):
    def __init__(self, *mappings, **kwargs):
        assert self == {}
        Dict.update(self, *mappings, **kwargs)

    def update(self, *mappings, **kwargs):
        for mapping in (mappings + (kwargs,)):
            if hasattr(mapping, 'keys'):
                for k in mapping:
                    self[k] = mapping[k]
            else:
                for (k,v) in mapping:
                    self[k] = v



-- 
Steve


More information about the Python-ideas mailing list