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

Steven D'Aprano steve at pearwood.info
Sat Feb 14 03:19:19 CET 2015


On Fri, Feb 13, 2015 at 08:02:56AM -0800, Chris Barker - NOAA Federal wrote:
> > On Feb 12, 2015, at 10:24 PM, Steven D'Aprano <steve at pearwood.info> wrote:
> >> += duplicates the extend method on lists.
> >
> > Yes it does, and that sometimes causes confusion when people wonder why
> > alist += blist is not *quite* the same as alist = alist + blist.
> 
> Actually, that's the primary motivator for += and friends -- to
> support in-place operations on mutables. Notably numpy arrays.

I'm not sure that implementing __iadd__ for numpy arrays is much harder 
than implementing an iadd method, and there isn't that much difference 
in readability between these two possible alternatives:

    data += some_numpy_array
    data.iadd(some_numpy_array)

If the motivation for augmented assignment was "numpy users don't want 
to type a method name", then I think that pandering to them was a poor 
decision.

I don't remember the discussion leading up to augmented assignment being 
added to the language, but I remember that *before* it was added there 
was a steady stream of people asking why they couldn't write:

    n += 1

like in C. (And a smaller number wanting to write ++n and n++ too, but 
fortunately nowhere near as many.)


[...]
> But the "problem" here is that augmented assignment shouldn't work on
> immutables at all.

That isn't the problem. Having augmented assignment work with immutables 
works fine. Even tuples can work perfectly:

py> t = ([1, 2, 3], "spam")
py> t += (None,)
py> t
([1, 2, 3], 'spam', None)

I might have been tempted to say that the *real* problem is mutables, 
not immutables, but even that is not the case:

py> t[0][2] += 1000
py> t
([1, 2, 1003], 'spam', None)

Perhaps the actual problem is that objects have no way of signalling to 
Python that they have, or have not, performed an in-place mutation, so 
that Python knows whether or not to try re-binding to the left hand 
reference. Or perhaps the real problem is that Python is so dynamic that 
it cannot tell whether a binding operation will succeed. Or that after a 
binding fails, that it cannot tell whether or not to catch and suppress 
the exception. There's no shortage of candidates for "the real problem".


> But then we wouldn't have the too appealing to resist syntactic sugar
> for integer incrementing.
> 
> But are you saying that augmented assignment was simply a mistake
> altogether, and therefore no new use of it should be added at all (
> and you'd deprecate it if you could)?

What's done is done. I certainly wouldn't deprecate it now. If += was a 
mistake, then fixing that mistake is more painful than living with it. 
Is it a mistake? I don't know. I guess that depends on whether you get 
more value from being able to write += then you get from having to deal 
with corner cases that fail.

The broader lesson from augmented assignment is that syntax and 
semantics are not entirely independent. Syntax that is unproblematic in 
C, with C's semantics, has painful corner cases with Python's semantics.

The narrower lesson is that I am cautious about using operators when a 
method or function can do. Saving a few keystrokes is not sufficient. 
Bringing it back to dicts:

    settings.printer_config.update(global_settings, user_settings)

is obvious and easy and will not fail if blob is a named tuple or 
printer_config is a read-only property (for example), while:

    settings.printer_config += global_settings + user_settings

may succeed and yet still raise an exception.


-- 
Steve


More information about the Python-ideas mailing list