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

Andrew Barnert abarnert at yahoo.com
Sat Feb 14 03:18:32 CET 2015


On Feb 13, 2015, at 18:02, Andrew Barnert <abarnert at yahoo.com.dmarc.invalid> wrote:

> On Feb 13, 2015, at 16:51, Rob Cliffe <rob.cliffe at btinternet.com> wrote:
> 
>> On 13/02/2015 06:19, Steven D'Aprano wrote:
>>> On Thu, Feb 12, 2015 at 07:43:36PM -0800, Chris Barker - NOAA Federal wrote:
>>>>> avoids any confusion over operators and having += duplicating the
>>>>> update method.
>>>> += 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. It also
>>> leads to a quite ugly and unfortunate language wart with tuples:
>>> 
>>> py> t = ([], None)
>>> py> t[0] += [1]
>>> Traceback (most recent call last):
>>>  File "<stdin>", line 1, in <module>
>>> TypeError: 'tuple' object does not support item assignment
>>> py> t
>>> ([1], None)
>>> 
>>> Try explaining to novices why this is not a bug.
>> Er, I'm not a novice, but why isn't that a bug?  I would have expected t to be altered as shown without raising an exception. (And given that the code does raise an exception, I find it surprising that it otherwise has the presumably intended effect.) t[0] is a list, not a tuple, so I would expect it to behave as a list:
> 
> Right, t[0] behaves like a list. But assigning (augmented or otherwise) to t[0] isn't an operation on the value t[0], it's an operation on the value t (in particular, `t.__setitem__(0, newval)`).
> 
> Augmented assignment does three things: it fetches the existing value, calls the __iadd__ operator on it (or __add__ if there's no __iadd__, which is how it works for immutable values), then stores the resulting new value. For this case, the first two steps succeed, and the third fails.
> 
> There's really no good alternative here. Either you don't allow augmented assignment, you don't allow it to work with immutable values (so `t=[0]; t[0] += 1` fails), or you add reference types and overloadable assignment operators and the whole mess that comes with them (as seen in C++) instead of Python's nifty complex assignment targets.
> 
>>>>> L = t[0]
>>>>> L += [2]
>>>>> t
>> ([1,2], None)
>> 
>> I was also curious why you say that alist += blist is not quite the same as alist = alist + blist.
>> (So far I've worked out that the first uses __iadd__ and the second uses __add__.  And there is probably a performance difference.  And the semantics could be different for objects of a custom class, but as far as I can see they should be the same for list objects.  What have I missed?)
> 
> Consider this case:
> 
>>>> clist = [0]
>>>> alist = clist
>>>> blist = [1]
> 
> Now, compare:
> 
>>>> alist = alist + blist
>>>> clist
>    [0]
> 
> ... versus:
> 
>>>> alist += blist
>>>> clist
>    [0, 1]
> 
> And that's because the semantics _are_ different for __add__ vs. __iadd__ for a list: the former creates and returns a new list, the latter modifies and returns self, and that's clearly visible (and often important to your code!) if there are any other references to self out there. That's the real reason we need __iadd__, not efficiency.
> 
>> I would appreciate it if you or someone else can find the time to answer.

I found an old partial explanation I'd started a year or two ago that you might find useful (or, more likely, you'll find it confusing, but then maybe you can help me improve the explanation for future readers...) and posted it online (http://stupidpythonideas.blogspot.com/2015/02/augmented-assignments-b.html).

In case you're wondering, it was originally intended as a StackOverflow answer to someone who wanted a more detailed explanation than the FAQ gives (and he brought up C++ for comparison), but then the question was closed before I could post it.


More information about the Python-ideas mailing list