Here is my code:
a = [1, 2, 3, 4, 5]
a[0], a[a[0]] = a[a[0]], a[0]
print(a)
I'm trying to swap a[0]
with a[a[0]]
(i.e. a[1]
in this case), so the result I expect is:
[2, 1, 3, 4, 5]
The result I get is [2, 2, 1, 4, 5]
, which is not what I want.
if I simplify a[0], a[a[0]] = a[a[0]], a[0]
to a[0], a[1] = a[1], a[0]
, it works.
How can I make this swap inside a list work like a, b = b, a
does?
That assignment's doing quite a lot. Let's break everything down …
a = [1, 2, 3, 4, 5]
Ok, that's the easy bit. Next:
a[0], a[a[0]] = a[a[0]], a[0]
The first thing that happens in any assignment is that the right hand side is evaluated, so:
a[a[0]], a[0]
reduces to a[1], a[0]
, which evaluates to (2, 1)
.
Then, each assignment target in turn gets one of those items from the right hand side assigned to it:
a[0] = 2 # 2 == first item in the (already evaluated) right hand side
Now that's done, a
looks like this:
[2, 2, 3, 4, 5]
Now we'll do the second assignment:
a[a[0]] = 1 # 1 == second item in the (already evaluated) right hand side
But wait! a[0]
is now 2
, so this reduces to
a[2] = 1
And, lo and behold, if we look at a
again, it's ended up as:
[2, 2, 1, 4, 5]
What you've discovered is that although Python claims to be able to swap two values simultaneously with e.g. a, b = b, a
, that isn't really true. It almost always works in practice, but if one of the values is part of the description of the other one – in this case, a[0]
is part of the description of a[a[0]]
– the implementation details can trip you up.
The way to fix this is to store the initial value of a[0]
before you start reassigning things:
a = [1, 2, 3, 4, 5]
tmp = a[0]
a[0], a[tmp] = a[tmp], a[0]
After which, a
looks the way you'd expect:
[2, 1, 3, 4, 5]