元组 (a,b)=(b,a) 中成员的交换在内部是如何进行的?
- 2024-12-26 08:43:00
- admin 原创
- 172
问题描述:
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 的值在内部是如何进行的?肯定不是使用临时变量。
解决方案 1:
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
在两个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
[d, c, b, a]
这里使用带有的堆栈来构建一个元组(以相反的顺序,BUILD_TUPLE
再次从堆栈中弹出,将结果元组推入堆栈),然后UNPACK_SEQUENCE
再次从堆栈中弹出元组,将元组中的所有元素再次推回堆栈以进行STORE_FAST
操作。
后者可能看起来像是一种浪费的操作,但赋值的右侧可能完全不同,可能是产生元组的函数调用,因此 Python 解释器不做任何假设,UNPACK_SEQUENCE
始终使用操作码。即使对于两个和三个名称的赋值操作,它也会这样做,但为了提高效率,后面的(窥孔)优化步骤会将具有2 个或 3 个参数的BUILD_TUPLE
/UNPACK_SEQUENCE
组合替换为上述操作码ROT_TWO
和ROT_THREE
操作码。
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD