обмен списком двух элементов завершился неудачно при повторном использовании значения в качестве индекса, чтобы избежать локальной переменной - PullRequest
2 голосов
/ 14 января 2020
l1=[0,2,1]
index=1
from ipdb import set_trace; set_trace()
l1[index], l1[l1[index]] = l1[l1[index]], l1[index]
print(l1)

почему l1 будет таким же? l1[1] и l1[2] не поменяются местами.

1 Ответ

3 голосов
/ 14 января 2020

Вы можете изменить порядок, и он будет работать:

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]
...