元组 (a,b)=(b,a) 中的成员交换在内部如何工作?

2024-03-09

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 的值交换在内部是如何工作的?它绝对不使用临时变量。


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操作码以提高效率。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

元组 (a,b)=(b,a) 中的成员交换在内部如何工作? 的相关文章

随机推荐