Python 将右侧表达式与左侧赋值分开。首先评估右侧,并将结果存储在堆栈中,然后使用取值的操作码分配左侧名称from再次堆栈。
对于具有 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 opcodes http://docs.python.org/library/dis.html#opcode-LOAD_FAST(将变量中的值推入堆栈),堆栈顶部保存[a, b]
. The ROT_TWO opcode http://docs.python.org/library/dis.html#opcode-ROT_TWO交换堆栈顶部的两个位置,因此堆栈现在有[b, a]
在顶部。他们俩STORE_FAST opcodes http://docs.python.org/library/dis.html#opcode-STORE_FAST然后获取这两个值并将它们存储在赋值左侧的名称中。首先STORE_FAST
弹出栈顶的一个值并将其放入a
,下一个再次弹出,将值存储在b
。需要旋转是因为 Python 保证左侧目标列表中的赋值是从左到右完成的。
对于 3 名分配,ROT_THREE http://docs.python.org/library/dis.html#opcode-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 http://docs.python.org/library/dis.html#opcode-BUILD_TUPLE再次从堆栈中弹出,将生成的元组推入堆栈),然后UNPACK_SEQUENCE http://docs.python.org/library/dis.html#opcode-UNPACK_SEQUENCE再次从堆栈中弹出元组,将元组中的所有元素再次推回到堆栈中STORE_FAST
运营。
后者可能看起来像是一个浪费的操作,但赋值的右侧可能是完全不同的东西,一个函数调用produces也许是一个元组,所以Python解释器不做任何假设并使用UNPACK_SEQUENCE
操作码始终。即使对于两个和三个名称赋值操作也是如此,但稍后(窥视孔)优化步骤 https://github.com/python/cpython/blob/85e415108226cc5f3fdddd70196fc4c2a1d0f7f4/Python/peephole.c#L323-L351取代一个BUILD_TUPLE
/ UNPACK_SEQUENCE
与上述 2 或 3 个参数的组合ROT_TWO
and ROT_THREE
操作码以提高效率。