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
intobjects do not even implement the__imul__hook; Python has to fall back to executingi = i * 3in 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.