在Python元组(A,B)=(B,A)内部工作如何交换的成员?在Python元组(A,B)=(B,

2019-05-08 23:32发布

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

请问这个交换内部的价值观和b的工作? 它绝对不使用临时变量。

Answer 1:

蟒分离从左手侧分配的右手边的表达。 第一右手侧进行评价,并且结果被存储在堆栈上,然后将左侧的名称使用的是再次采取值堆栈操作码赋值。

对于具有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操作码始终。 它这样做,即使是二,三名赋值操作, 但是稍后的(窥视孔)优化步骤代替了BUILD_TUPLE / UNPACK_SEQUENCE有2个或3个参数与上述组合ROT_TWOROT_THREE操作码效率。



文章来源: How does swapping of members in the python tuples (a,b)=(b,a) work internally?