
On Fri, Feb 13, 2015 at 12:06:44AM +0000, Andrew Barnert wrote:
On Thursday, February 12, 2015 1:33 PM, Nathan Schneider <neatnate@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