当前位置: 首页 > 面试题库 >

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

胥良平
2023-03-14
问题内容
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将右侧表达式与左侧赋值分开。首先评估右侧,并将结果存储在堆栈中,然后使用再次从堆栈中获取值的操作码分配左侧名称。

对于具有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操作码的效率。



 类似资料:
  • 问题内容: 这是我的第一个问题,我开始学习Python。之间有什么区别: 和 在下面的示例中编写时,它显示不同的结果。 和 问题答案: 在中,在将右侧的表达式赋给左侧之前对其求值。因此,它等效于: 在第二个示例中,运行时已更改的值。因此,结果是不同的。

  • 然而,今天我在处理一些代码时,意外地发现以下两个交换给出了不同的结果: 这让我难以置信。有人能给我解释一下这里发生了什么吗?

  • 然而,今天我在处理一些代码时,意外地发现以下两个交换给出了不同的结果: 这让我难以置信。有人能给我解释一下这里发生了什么吗?

  • 问题内容: 这是我必须弄清楚怎么可能的代码。我有一个线索,但我不知道该怎么做。我认为这与负数和正数有关,也可能与变量修饰符有关。我是一个初学者,我到处都看过解决方案,但是找不到可用的东西。 问题是:您需要声明和初始化两个变量。如果条件必须为真。 代码: 感谢您抽出宝贵的时间。 问题答案: 这对于基本类型是不可能的。您可以使用带框的整数来实现: 在和比较将使用未装箱的值1,而将比较引用,并会成功,因

  • 我尝试了一些代码在Java中交换两个整数,而不使用第三个变量,即使用XOR。 以下是我尝试的两个交换函数: 该代码产生的输出如下: 我很想知道,为什么会有这样的说法: 和这个不一样?

  • 问题内容: 我尝试了一些代码,使用XOR在Java中交换两个整数而不使用第三个变量。 这是我尝试的两个交换函数: 这段代码产生的输出是这样的: 我很好奇,为什么这样说: 与这个不同吗? 问题答案: 问题是评估的顺序: 参见JLS第15.26.2节 首先,对左操作数求值以产生一个变量。 如果该评估突然完成,则赋值表达式由于相同的原因而突然完成;右边的操作数不会被评估,并且不会发生赋值。 否则,将保存