如何交换python元组(a,b)=(b,a)中的成员在内部工作?
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的值在内部工作? 它绝对不使用临时variables。
Python将右侧expression式从左侧分配中分离出来。 首先对右侧进行评估,并将结果存储在堆栈中,然后使用从堆栈中再次获取值的操作码分配左侧名称。
对于2或3项的元组赋值,Python只是直接使用堆栈:
>>> 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
操作码之后 (将一个variables的值压入堆栈),堆栈顶部保存[a, b]
。 ROT_TWO
操作码交换堆栈中的前两个位置,所以堆栈现在在顶部有[b, a]
。 然后两个STORE_FAST
操作码将这两个值存储在赋值左侧的名称中。 第一个STORE_FAST
popup堆栈顶部的值并将其放入a
,下一个popup,将值存储在b
。 因为Python确保左侧的目标列表中的分配从左到右完成,所以需要旋转。
对于一个3个名字的赋值, ROT_THREE
后跟ROT_TWO
被执行来颠倒堆栈中的前三个项目。
对于更长的左侧分配,构build一个显式元组:
>>> 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一个元组(按照相反的顺序, BUILD_TUPLE
再次从栈中popup,并将结果推送到栈上),然后UNPACK_SEQUENCE
再次从栈中popup元组,将所有元素从元组返回到栈上再次进行STORE_FAST
操作。
后者可能看起来像是一个浪费的操作,但是赋值的右边可能是完全不同的东西,也许是产生一个元组的函数调用,所以Python解释器不做任何假设,并总是使用UNPACK_SEQUENCE
操作码。 即使对于两个和三个名字的赋值操作,它也这样做, 但是稍后的(窥孔)优化步骤用上面的ROT_TWO
和ROT_THREE
操作码replace具有2或3个参数的BUILD_TUPLE
/ UNPACK_SEQUENCE
组合以提高效率。