这是我的代码:
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
之类的列表中进行此交换呢?
这项任务做了很多。让我们把一切都打破......
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]
python系统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