[Python-ideas] Dict joining using + and +=

Steven D'Aprano steve at pearwood.info
Tue Mar 5 04:28:33 EST 2019

On Mon, Mar 04, 2019 at 09:34:34PM +0000, Paul Moore wrote:
> On Mon, 4 Mar 2019 at 20:42, Guido van Rossum <guido at python.org> wrote:
> >
> > Honestly I would rather withdraw the subtraction operators than 
> > reopen the discussion about making dict more like set.

As some people have repeatedly pointed out, we already have four ways 
to spell dict merging:

- in-place dict.update;
- copy, followed by update;
- use a ChainMap;
- the obscure new (**d1, ...} syntax.

But if there's a good way to get dict difference apart from a manual 
loop or comprehension, I don't know it.

So from my perspective, even though most of the attention has been on 
the merge operator, I'd rather keep the difference operator.

As far as making dicts "more like set", I'm certainly not proposing 
that. The furthest I'd go is bow to the consensus if it happened to 
decide that | is a better choice than + (but that seems unlikely).

> I'm neutral on dict addition, but dict subtraction seemed an odd
> extension to the proposal. Using b in a - b solely for its keys, and
> ignoring its values, seems weird to me. 

The PEP current says that dict subtraction requires the right-hand 
operand to be a dict. That's the conservative choice that follows the 
example of list addition (it requires a list, not just any iterable) and 
avoids breaking changes to code that uses operator-overloading:

    mydict - some_object

works if some_object overloads __rsub__. If dict.__sub__ was greedy in 
what it accepted, it could break such code. Better (in my opinion) to be 
less greedy by only allowing dicts.

dict -= on the other hand can take any iterable of keys, as the 
right-hand operand isn't called.

Oh, another thing the PEP should gain... a use-case for dict 
subtraction. Here's a few:

(1) You have a pair of dicts D and E, and you want to update D with only 
the new keys from E:

    D.update(E - D)

which I think is nicer than writing a manual loop:

    D.update({k:E[k] for k in (E.keys() - D.keys())})
    # or
    D.update({k:v for k,v in E.items() if k not in D})

(This is a form of update with "first seen wins" instead of the usual 
"last seen wins".)

(2) You have a dict D, and you want to unconditionally remove keys from 
a blacklist, e.g.:

    all_users = {'username': user, ...}
    allowed_users = all_users - banned_users

(3) You have a dict, and you want to ensure there's no keys that 
you didn't expect:

    if (d := actual-expected):
        print('unexpected key:value pairs', d)

> Even if dict1 - dict2 were
> added to the language, I think I'd steer clear of it as being too
> obscure.

Everything is obscure until people learn it and get used to it.


More information about the Python-ideas mailing list