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:
(bold emphasis mine).
This is achieved by letting objects implement
__i[op]__
methods, for=*
that's the__imul__
hook: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 executingi = i * 3
in that case.So for the first example, the code:
really does this (with the loop unrolled for illustration purposes):
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:
So now the new integer objects are assigned to
i
, which is independent froma
.