再次使用该值作为索引以避免局部变量时,列表交换两个元素失败

人气:54 发布:2023-01-03 标签: python list swap

问题描述

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]不会交换。

推荐答案

您可以更改顺序,它会起作用:

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
在这种类型的赋值中,首先计算表达式的右侧(请参见Evaluation Order)。因此,首先,指令集(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.
现在,您可以对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.

因此,当您通过将列表元素作为索引传递来访问索引时,最好避免这种赋值。而是Explicit:

l1 = [0, 2, 1]
index1 = 1
index2 = l1[index1]
l1[index1], l1[index2] = l1[index2], l1[index1]
print(l1)
# [0, 1, 2]

21