Running this:
a = [[1], [2]]
for i in a:
i *= 2
print(a)
Gives
[[1, 1], [2, 2]]
I would expect to get the original list, as happens here:
a = [1, 2]
for i in a:
i *= 2
print(a)
Which gives:
[1, 2]
Why is the list in the first example being modified?
You are using augmented assignment statements. These operate on the object named on the left-hand side, giving that object the opportunity to update in-place:
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.
(bold emphasis mine).
This is achieved by letting objects implement __i[op]__
methods, for =*
that's the __imul__
hook:
These methods are called to implement the augmented arithmetic assignments (
+=
,-=
,*=
,@=
,/=
,//=
,%=
,**=
,<<=
,>>=
,&=
,^=
,|=
). These methods should attempt to do the operation in-place (modifyingself
) and return the result (which could be, but does not have to be,self
).
Using *=
on a list multiplies that list object and returns the same list object (self
) to be 'assigned' back to the same name.
Integers on the other hand are immutable objects. Arithmetic operations on integers return new integer objects, so int
objects do not even implement the __imul__
hook; Python has to fall back to executing i = i * 3
in that case.
So for the first example, the code:
a = [[1], [2]]
for i in a:
i *= 2
really does this (with the loop unrolled for illustration purposes):
a = [[1], [2]]
i = a[0].__imul__(2) # a[0] is altered in-place
i = a[1].__imul__(2) # a[1] is altered in-place
where the list.__imul__
method applies the change to the list object itself, and returns the reference to the list object.
For integers, this is executed instead:
a = [1, 2]
i = a[0] * 2 # a[0] is not affected
i = a[1] * 2 # a[1] is not affected
So now the new integer objects are assigned to i
, which is independent from a
.