Augument assignment versus regular assignment

Antoon Pardon apardon at forel.vub.ac.be
Mon Jul 10 09:23:56 EDT 2006


On 2006-07-10, Jim Segrave <jes at nl.demon.net> wrote:
> In article <slrneb48qm.rg3.apardon at rcpc42.vub.ac.be>,
> Antoon Pardon  <apardon at forel.vub.ac.be> wrote:
>>On 2006-07-09, Fredrik Lundh <fredrik at pythonware.com> wrote:
>>> Frank Millman wrote:
>>>
>>>> So it looks as if x +=  [] modifies the list in place, while x = x + []
>>>> creates a new list.
>>>
>>> objects can override the += operator (by defining the __iadd__ method), 
>>> and the list type maps __iadd__ to extend.  other containers may treat 
>>> += differently, but in-place behaviour is recommended by the language
>>> reference:
>>>
>>>    An augmented assignment expression like x += 1 can be rewritten as
>>>    x = x + 1 to achieve a similar, but not exactly equal effect.  In
>>>    the augmented version, x is only evaluated once. Also, when possible,
>>>    the actual operation is performed in-place, meaning that rather than
>>>    creating a new object and assigning that to the target, the old object
>>>    is modified instead.
>>
>>What does it mean that x is only evaluated once.  I have an avltree module,
>>with an interface much like a directory.  So I added print statements to
>>__setitem__ and __getitem__ and then tried the following code.
>>
>>>>> from avltree import Tree
>>>>> t=Tree()
>>>>> t['a'] = 1
>>__setitem__, key = a
>>>>> t['a']
>>__getitem__, key = a
>>1
>>>>> t['a'] = t['a'] + 1
>>__getitem__, key = a
>>__setitem__, key = a
>>>>> t['a'] += 1
>>__getitem__, key = a
>>__setitem__, key = a
>>>>> t['b'] = []
>>__setitem__, key = b
>>>>> t['b'] = t['b'] + [1]
>>__getitem__, key = b
>>__setitem__, key = b
>>>>> t['b'] += [2]
>>__getitem__, key = b
>>__setitem__, key = b
>>
>>So to me it seems that when we substitute t['a'] or t['b'] for x,
>>x is evaluated twice with the augmented version, just like it
>>is with the not augmented version.
>
> $ cat x.py
>
> def getindex(ind = 0):
>     print 'getindex() called'
>     return ind
>
> a = [0, 1, 2, 3, 4, 5]
> a[getindex(0)] = a[getindex(0)] + 17
> print a
> a[getindex(1)] += 22
> print a
>
> $ python x.py
> getindex() called
> getindex() called
> [17, 1, 2, 3, 4, 5]
> getindex() called
> [17, 23, 2, 3, 4, 5]
>
> In this case, getindex() is a rather pointless function, but it could
> be an expensive one or one with side effects or even one which alters
> state, so that it gives different values on subsequent calls with the
> same argument.
>
> The += version finds the object to be operated upon once, the expanded
> version does it twice.

I disagree. The += version only evaluates the index once, but still has
to find the object twice.

Let us start with the following:

t['b'] = []

Now with x being evaluated once, I would expect that

  t[getindex('b')] += [1]

would be equivallent to:

  _key = getindex('b')
  _lst = t[_key]
  _lst += [1]

And not to the following:

  _key = getindex('b')
  _lst = t[_key]
  _lst += [1]
   t[_key] =  _lst


But as far as I can interpret what is happening from the printed lines
the second is happening and not the first. So in this example some
optimisation has happened, by calculating the key only once, but
the search for the object using that precalculated key happens twice,
once with __getitem__ and once with __setitem__.

>>> t['b'] = []
__setitem__, key = b
>>> t[getindex('b')] += [1]
getindex() called
__getitem__, key = b
__setitem__, key = b

-- 
Antoon Pardon



More information about the Python-list mailing list