1

I always thought x += 1 was just syntactic shorthand (and exactly equivalent to) x = x + 1, until I spent a while trying to figure out why this code wasn't acting as intended:

[ipython/euler 72 ]$ def func(mylist):
    mylist += random.sample(range(100),2)
    # do stuff with the random result, then restore original list
    mylist = mylist[:-2]

It's supposed to return the same list it gets, but it doesn't seem to work that way:

[ipython/euler 81 ]$ x = [1,2,3]

[ipython/euler 82 ]$ func(x)
[1, 2, 3, 23, 7]

[ipython/euler 83 ]$ func(x)
[1, 2, 3, 23, 7, 42, 36]

[ipython/euler 84 ]$ func(x)
[1, 2, 3, 23, 7, 42, 36, 0, 5]

If I change the assignment statement to the long form mylist = mylist + ..., it works as expected and leaves the list unchanged.

Why is this happening? I assume it has to do with lists being mutable and possibly iadd not being 'real' addition when called as an overloaded method of list, but I still expected the interpreter to see them as equivalent.

grapes
  • 66
  • 6

2 Answers2

4

The line

mylist += random.sample(range(100),2)

mutates the list mylist in-place (that's why it's called iadd: in-place add). This means that it changes the original list in the caller's scope.

mylist = mylist[:-2]

creates a new local object mylist and assigns the contents of global mylist[:-2] to it. This new local object is then discarded immediately upon returning from the function.

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • 3
    Tempted to downvote because of the remark about `global`... If he had added `global mylist`, his original list would still be called `x` and thus the code would definitely not behave as expected; and using globals for this is certainly not the way to go. You'd either want to copy the list at the beginning or modify it in-place all the way through, e.g. using `pop` twice instead of slicing at the end. – l4mpi Sep 19 '13 at 10:45
  • @l4mpi: You're right, I should remove this. Thanks. – Tim Pietzcker Sep 19 '13 at 11:06
4

The first code (mylist += random.sample(range(100),2)) modifies the list in place, as you've realised.

Both of these:

mylist = mylist + ...
mylist = mylist[:-2]

create a new object called mylist, which is then given the same value as (or one derived from) the previous mylist.

The latter line does not restore the original list for this reason; it's creating a new copy, and leaving the original reference alone.

You can get your desired behaviour by changing the list in place:

mylist[:] = mylist[:-2]
sapi
  • 9,944
  • 8
  • 41
  • 71