Reputation: 53
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?
Upvotes: 5
Views: 123
Reputation: 59118
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]
Upvotes: 2
Reputation: 223
python system lib dis
module may help. The dis
module supports the analysis of CPython bytecode by disassembling it. you can disassembling it to see how the swap works internally.
In [1]: import dis
In [2]: def func():
...: a = [1, 2, 3, 4, 5]
...: a[0], a[a[0]] = a[a[0]], a[0]
...: print a
In [3]: func()
[2, 2, 1, 4, 5]
In [4]: dis.dis(func)
2 0 LOAD_CONST 1 (1)
3 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
9 LOAD_CONST 4 (4)
12 LOAD_CONST 5 (5)
15 BUILD_LIST 5
18 STORE_FAST 0 (a) # make list: a = [1, 2, 3, 4, 5]
3 21 LOAD_FAST 0 (a) # stack: a
24 LOAD_FAST 0 (a) # stack: a|a
27 LOAD_CONST 6 (0) # stack: a|a|0
30 BINARY_SUBSCR # stack: a|1
31 BINARY_SUBSCR # stack: 2
32 LOAD_FAST 0 (a) # stack: 2|a
35 LOAD_CONST 6 (0) # stack: 2|a|0
38 BINARY_SUBSCR # stack: 2|1
39 ROT_TWO # stack: 1|2
40 LOAD_FAST 0 (a) # stack: 1|2|a
43 LOAD_CONST 6 (0) # stack: 1|2|a|0
46 STORE_SUBSCR # stack: 1| a: a[0] = 2
47 LOAD_FAST 0 (a) # stack: 1|a
50 LOAD_FAST 0 (a) # stack: 1|a|a
53 LOAD_CONST 6 (0) # stack: 1|a|a|0
56 BINARY_SUBSCR # stack: 1|a|2
57 STORE_SUBSCR # stack: a: a[2] = 1
4 58 LOAD_FAST 0 (a)
61 PRINT_ITEM
62 PRINT_NEWLINE
63 LOAD_CONST 0 (None)
66 RETURN_VALUE
https://docs.python.org/3/library/dis.html
Upvotes: 0