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

Posted

技术标签:

【中文标题】元组 (a,b)=(b,a) 中的成员交换如何在内部工作?【英文标题】:How does swapping of members in tuples (a,b)=(b,a) work internally? 【发布时间】:2014-01-29 14:46:20 【问题描述】:
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 值的交换如何在内部进行?它绝对没有使用临时变量。

【问题讨论】:

您可能会对使用dis 查看代码的反汇编感兴趣。 Spoliers:使用了字节码指令ROT_TWO 【参考方案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 opcodes(将变量中的值压入堆栈)之后,堆栈顶部为[a, b]ROT_TWO opcode 交换堆栈顶部的两个位置,因此堆栈现在顶部有 [b, a]。然后两个STORE_FAST opcodes 获取这两个值并将它们存储在赋值左侧的名称中。第一个STORE_FAST将栈顶的值弹出并放入a,下一个再次弹出,将值存储在b中。需要旋转是因为 Python 保证左侧目标列表中的分配是从左到右完成的。

对于 3-name 赋值,执行 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 操作码。即使对于二名和三名赋值操作,but a later (peephole) optimization step 也会使用上述 ROT_TWOROT_THREE 操作码将 BUILD_TUPLE / UNPACK_SEQUENCE 组合替换为 2 或 3 个参数以提高效率。

【讨论】:

+1 for 需要旋转,因为 Python 保证左侧目标列表中的分配是从左到右完成的。。我不知道。 这个解释的 ELI5 是什么?

以上是关于元组 (a,b)=(b,a) 中的成员交换如何在内部工作?的主要内容,如果未能解决你的问题,请参考以下文章

python基础语法python实现交换操作a,b = b,a的原理

使用元组交换两个数据

交换两个变量

python元组到dict

华为交换机12804堆叠配置

如何声明两个类以使 A 具有 B 的成员和 B 将 A 的成员标记为朋友?