Вы можете изменить порядок, и он будет работать:
l1=[0,2,1]
index=1
l1[l1[index]], l1[index] = l1[index], l1[l1[index]]
print(l1)
Вывод:
[0, 1, 2]
Итак, давайте сначала посмотрим на разборку вашего кода:
import dis
def switch():
l1=[0,2,1]
index=1
l1[index], l1[l1[index]] = l1[l1[index]], l1[index]
return l1
dis.dis(switch)
2 0 LOAD_CONST 1 (0)
2 LOAD_CONST 2 (2)
4 LOAD_CONST 3 (1)
6 BUILD_LIST 3
8 STORE_FAST 0 (l1)
3 10 LOAD_CONST 3 (1)
12 STORE_FAST 1 (index)
5 14 LOAD_FAST 0 (l1)
16 LOAD_FAST 0 (l1)
18 LOAD_FAST 1 (index)
20 BINARY_SUBSCR
22 BINARY_SUBSCR
24 LOAD_FAST 0 (l1)
26 LOAD_FAST 1 (index)
28 BINARY_SUBSCR
30 ROT_TWO
32 LOAD_FAST 0 (l1)
34 LOAD_FAST 1 (index)
36 STORE_SUBSCR
38 LOAD_FAST 0 (l1)
40 LOAD_FAST 0 (l1)
42 LOAD_FAST 1 (index)
44 BINARY_SUBSCR
46 STORE_SUBSCR
6 48 LOAD_FAST 0 (l1)
50 RETURN_VALUE
В этом типе присваивания правая часть выражения вычисляется первой (см. Порядок оценки ). Итак, сначала набор инструкций (14 - 18)
загружает l1[index]
, то есть 1
, и помещает его в стек. Затем 24-26
загружает l1[l1[index]]
, т.е. 2
и помещает его в стек. Таким образом, стек теперь содержит [2,1]
. ROT_TWO
(30) меняет местами стек и делает его [1, 2]
, порядок, который нам нужен.
Теперь, в 32 - 36, вершина стека, то есть 1
присваивается l1[index]
, так что теперь l1[index] == 1
, то есть l1[1] = 1
.
Затем 38 - 42, оставшийся элемент в стеке, т.е. 2
, перемещается в l1[l1[index]]
, но теперь значение l1[index]
равно 1, так что вы, по сути, делаете, l1[1] = 1
. Итак, давайте посмотрим:
l1[index], l1[l1[index]] = l1[l1[index]], l1[index]
loaded == 2, 1
after stack swapping == 1, 2
l1[1] == 1
l1[1] == 2
# So you have modified only index 1, and then overwritten it with its original value.
Примерно так:
14 LOAD_FAST 0 (l1) ¯¯|
16 LOAD_FAST 0 (l1) ¯¯| 2 | 1 ---------->
18 LOAD_FAST 1 (index)__| __| ↓
20 BINARY_SUBSCR |
22 BINARY_SUBSCR |
24 LOAD_FAST 0 (l1) ¯¯| 2 ------------------------>
26 LOAD_FAST 1 (index)__| | ↓
28 BINARY_SUBSCR | |
30 ROT_TWO | |
32 LOAD_FAST 0 (l1) ¯¯| ↓ |
34 LOAD_FAST 1 (index)__| l1[1] = 1 <-------- |
36 STORE_SUBSCR | |
38 LOAD_FAST 0 (l1) | ¯¯| |
40 LOAD_FAST 0 (l1) ¯¯| ↓ | |
42 LOAD_FAST 1 (index)__| l1[1] == 1 __| l1[1] = 2 <---
44 BINARY_SUBSCR
46 STORE_SUBSCR
Если мы следуем той же логике в моем решении:
l1[l1[index]], l1[index] = l1[index], l1[l1[index]]
loaded = 1, 2
after stack swapping == 2, 1
l1[2] = 2
l1[1] = 1
# Here, as you have not changed the value of `l1[index]` in the first assignment, the order remains.
Теперь вы можете следовать те же логики c для l1 = [0, 1, 2]
. Хотя это и не нуждается в объяснении, поскольку оба l1[index]
и l1[l1[index]]
одинаковы:
l1 = [0, 1, 2]
l1[index], l1[l1[index]] = l1[l1[index]], l1[index]
loaded = 1, 1
after stack swapping == 1, 1
l1[1] == 1
l1[1] == 1
------------------------------------------------------------------
l1[l1[index]], l1[index] = l1[index], l1[l1[index]]
loaded = 1, 1
after stack swapping == 1, 1
l1[1] = 1
l1[1] = 1
# Here both have same value, so it does not modify.
Так что, когда вы получаете доступ к индексам, передавая элемент списка в качестве индекса, лучше избегать такого рода назначения. Вместо этого будьте Явным :
l1 = [0, 2, 1]
index1 = 1
index2 = l1[index1]
l1[index1], l1[index2] = l1[index2], l1[index1]
print(l1)
# [0, 1, 2]