Augmented assignment (was Re: Something in the function tutorial confused me.)

Aahz aahz at pythoncraft.com
Sat Aug 11 16:36:59 CEST 2007


In article <bAjvi.47120$wv5.1434653 at phobos.telenet-ops.be>,
Roel Schroeven  <rschroev_nospam_ml at fastmail.fm> wrote:
>Aahz schreef:
>> 
>> Although Alex is essentially correct, the situation is a bit more complex
>> and you are correct that augmented assignment allows the object to decide
>> whether to mutate in place.  However, the critical part of Alex's point
>> is what you need to focus on: it's the *tuple* in Alex's example that
>> intercepts the assignment call, not the list contained in the tuple.
>> 
>> Obviously, you can easily work around it:
>> 
>>>>> t = ([],)
>>>>> l = t[0]
>>>>> l += ['foo']
>>>>> t
>> (['foo'],)
>
>I'm missing something here, I think. As far as I can tell, the language 
>reference says that the target is evaluated before the augmented 
>assignment is performed. If that's the case, I don't see how the tuple 
>in Alex' example has anything to with it: the assignment is to the list, 
>not the tuple.
>
>And watch this (Python 2.4.2):
>
> >>> tup = ([],)
> >>> tup[0] += ['zap']
>
>Traceback (most recent call last):
>   File "<pyshell#10>", line 1, in -toplevel-
>     tup[0] += ['zap']
>TypeError: object does not support item assignment
>
>So far that's the same as Alex' example, but look at the value of the 
>tuple now:
>
> >>> tup
>(['zap'],)
>
>Despite the TypeError, the list *has* changed.

Yup!  Here's why:

>>> def foo(bar): bar[0] += ['zap']
... 
>>> import dis
>>> dis.dis(foo)
  1           0 LOAD_FAST                0 (bar)
              3 LOAD_CONST               1 (0)
              6 DUP_TOPX                 2
              9 BINARY_SUBSCR       
             10 LOAD_CONST               2 ('zap')
             13 BUILD_LIST               1
             16 INPLACE_ADD         
             17 ROT_THREE           
             18 STORE_SUBSCR        
             19 LOAD_CONST               0 (None)
             22 RETURN_VALUE        

Notice the critical sequence: BINARY_SUBSCR, INPLACE_ADD, STORE_SUBSCR.
It has to work that way to allow this:

>>> l = [7]
>>> l[0] += 1
>>> l
[8]

There's simply no way to get augmented assignment to work correctly with
both lists and tuples when you allow both mutable and immutable elements.
Someone will always get surprised, and overall with strings and numbers
being the canonical list elements, I think making augmented assignment
work correctly with lists and immutables was the best decision.

But the unfortunate side-effect is that you get the TypeError with
augmented assignment in tuples only after the mutation has been done.
-- 
Aahz (aahz at pythoncraft.com)           <*>         http://www.pythoncraft.com/

"And if that makes me an elitist...I couldn't be happier."  --JMS



More information about the Python-list mailing list