Python将右侧表达式与左侧赋值分开。首先计算右侧,结果存储在堆栈中,然后使用操作码分配左侧名称,这些操作码再次从堆栈中获取值。
对于包含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
在两个^{} opcodes(将一个值从变量推送到堆栈上)之后,堆栈顶部保存[a, b]。^{} opcode交换堆栈顶部的两个位置,因此堆栈现在在顶部有[b, a]。然后,这两个^{} opcodes接受这两个值,并将它们存储在赋值左侧的名称中。第一个STORE_FAST弹出堆栈顶部的值并将其放入a,下一个再次弹出,将值存储在b。之所以需要旋转,是因为Python保证从左到右完成左侧目标列表中的赋值。
对于3-name赋值,执行^{}后跟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]的堆栈来构建一个元组(按相反的顺序,再次从堆栈弹出^{},将生成的元组推到堆栈上),然后^{}再次从堆栈弹出元组,再次将元组中的所有元素推回到堆栈上进行STORE_FAST操作。
后者看起来是一个浪费的操作,但是赋值的右边可能是完全不同的,一个函数调用可能会产生一个元组,所以Python解释器不做任何假设,总是使用UNPACK_SEQUENCE操作码。即使对于两个和三个名称分配操作,but a later (peephole) optimization step也会使用上面的ROT_TWO和ROT_THREE操作码将BUILD_TUPLE/UNPACK_SEQUENCE组合替换为2个或3个参数,以提高效率。