Почему мой код для замены двух элементов списка работает неправильно? - PullRequest
0 голосов
/ 01 марта 2019

Вот мой код:

a = [1, 2, 3, 4, 5]
a[0], a[a[0]] = a[a[0]], a[0]
print(a)

Я пытаюсь поменять a[0] на a[a[0]] (т.е. a[1] в данном случае), поэтому ожидаемый результат:

[2, 1, 3, 4, 5]

В результате я получаю [2, 2, 1, 4, 5], а это не то, что я хочу.

, если я упросту a[0], a[a[0]] = a[a[0]], a[0] до a[0], a[1] = a[1], a[0], это сработает.

Как можноЯ делаю этот обмен внутри списка, как a, b = b, a?

Ответы [ 2 ]

0 голосов
/ 01 марта 2019
Модуль

Python System lib dis может помочь.Модуль dis поддерживает анализ байт-кода CPython путем его разборки.Вы можете разобрать его, чтобы увидеть, как своп работает внутри.

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

0 голосов
/ 01 марта 2019

Это задание делает довольно много.Давайте разберемся со всем ...

a = [1, 2, 3, 4, 5]

Хорошо, это легко.Далее:

a[0], a[a[0]] = a[a[0]], a[0]

Первое, что происходит в любом задании, - это оценка правой части, поэтому:

a[a[0]], a[0] уменьшается до a[1], a[0], что оценивается до (2, 1).

Затем каждая цель назначения по очереди получает один из этих предметов с назначенной ей правой стороны:

a[0] = 2   # 2 == first item in the (already evaluated) right hand side

Теперь все готово, a выглядит следующим образом:

[2, 2, 3, 4, 5]

Теперь мы выполним второе задание:

a[a[0]] = 1   # 1 == second item in the (already evaluated) right hand side

Но подождите!a[0] теперь 2, так что это сокращается до

a[2] = 1

И, о чудо, если мы посмотрим на a снова, это закончится как:

[2, 2, 1, 4, 5]

Вы обнаружили, что хотя Python утверждает, что он может поменять два значения одновременно, например, a, b = b, a, это не совсем так.На практике это почти всегда работает, но если одно из значений является частью описания другого - в этом случае a[0] является частью описания a[a[0]] - детали реализации могут сбить вас с толку.

Способ исправить это - сохранить начальное значение a[0] до того, как вы начнете переназначать вещи:

a = [1, 2, 3, 4, 5]
tmp = a[0]
a[0], a[tmp] = a[tmp], a[0]

После чего a выглядит так, как вы ожидаете:

[2, 1, 3, 4, 5]
...