In [55]: a = 5
In [56]: b = 6
In [57]: (a, b) = (b, a)
In [58]: a
Out[58]: 6
In [59]: b
Out[59]: 5
这个交换a和b的值是如何在内部工作的?它绝不是使用一个临时变量。
In [55]: a = 5
In [56]: b = 6
In [57]: (a, b) = (b, a)
In [58]: a
Out[58]: 6
In [59]: b
Out[59]: 5
这个交换a和b的值是如何在内部工作的?它绝不是使用一个临时变量。
>>> import dis
>>> def foo(a, b):
... a, b = b, a
...
>>> dis.dis(foo)
2 0 LOAD_FAST 1 (b)
3 LOAD_FAST 0 (a)
6 ROT_TWO
7 STORE_FAST 0 (a)
10 STORE_FAST 1 (b)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
LOAD_FAST
操作码(将变量的值推送到堆栈上)之后,堆栈顶部保存着 [a, b]
。ROT_TWO
操作码 交换堆栈上的前两个位置,因此堆栈顶部现在是 [b, a]
。然后,两个 STORE_FAST
操作码 将这两个值存储在赋值左侧的名称中。第一个 STORE_FAST
弹出堆栈顶部的值并将其放入 a
中,接下来再次弹出并将该值存储在 b
中。需要旋转是因为 Python 保证在左侧目标列表中的赋值是从左到右完成的。ROT_THREE
,然后再执行ROT_TWO
,以将栈顶的前三个元素反转。>>> def bar(a, b, c, d):
... d, c, b, a = a, b, c, d
...
>>> dis.dis(bar)
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 LOAD_FAST 2 (c)
9 LOAD_FAST 3 (d)
12 BUILD_TUPLE 4
15 UNPACK_SEQUENCE 4
18 STORE_FAST 3 (d)
21 STORE_FAST 2 (c)
24 STORE_FAST 1 (b)
27 STORE_FAST 0 (a)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
这里使用包含[d, c, b, a]的堆栈来构建元组(按相反顺序,BUILD_TUPLE
再次从堆栈中弹出,将结果元组推送回堆栈),然后 UNPACK_SEQUENCE
再次从堆栈中弹出元组,并将所有元素从元组中推送回堆栈以进行 STORE_FAST
操作。
后者可能看起来像一种浪费的操作,但是赋值语句右侧可能是完全不同的东西,例如调用一个生成元组的函数,因此Python解释器不会做出任何假设,并始终使用UNPACK_SEQUENCE
操作码。即使对于两个或三个名称的赋值操作,但稍后(peephole)优化步骤也会将具有2个或3个参数的BUILD_TUPLE
/UNPACK_SEQUENCE
组合替换为上述的ROT_TWO
和ROT_THREE
操作码以达到更高的效率。
dis
查看代码的反汇编感兴趣。剧透:使用了字节码指令ROT_TWO
。 - Kevin