pra*_*een 27 python tuples python-internals iterable-unpacking
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
Run Code Online (Sandbox Code Playgroud)
如何交换a和b的值在内部工作?它肯定不使用临时变量.
Mar*_*ers 75
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
Run Code Online (Sandbox Code Playgroud)
在两个LOAD_FAST操作码(将变量中的值推入堆栈)之后,堆栈顶部成立[a, b].该ROT_TWO码是交换堆栈顶部的两个位置,使堆积现在有[b, a]在顶部.然后,这两个STORE_FAST操作码将这两个值存储在赋值左侧的名称中.第一个STORE_FAST弹出堆栈顶部的值并将其放入a,然后再次弹出,将值存储在其中b.需要轮换,因为Python保证左侧目标列表中的分配从左到右完成.
对于3名称赋值,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
Run Code Online (Sandbox Code Playgroud)
这里堆栈[d, c, b, a]用于构建一个元组(以相反的顺序,BUILD_TUPLE再次从堆栈中弹出,将生成的元组推入堆栈),然后UNPACK_SEQUENCE再次从堆栈中弹出元组,将所有元素从元组推回到再次堆叠STORE_FAST操作.
后者可能看起来像是一个浪费的操作,但是赋值的右侧可能是完全不同的东西,也许是一个产生元组的函数调用,因此Python解释器不做任何假设并UNPACK_SEQUENCE始终使用操作码.即使对于两个和三个名称的赋值操作也是这样,但是后来的(窥视孔)优化步骤用上面的2或3个参数替换BUILD_TUPLE/ UNPACK_SEQUENCE组合,ROT_TWO并且ROT_THREE效率的操作码.
| 归档时间: |
|
| 查看次数: |
11142 次 |
| 最近记录: |