首页 > 解决方案 > 为什么这个交换表达式不能按预期工作?

问题描述

这是我尝试用 Python 编写的基本选择排序。

arr = [64,12,35,312,45,12,50,80,0,4,6,8,92]
for i in range(len(arr)):
    arr[i], arr[arr.index(min(arr[i:]))] = arr[arr.index(min(arr[i:]))], arr[i]

但是,交换表达式不起作用。很明显,这里有一些我不知道的关于 Python 的东西。它是什么?

标签: python

解决方案


这是因为首先计算 rhs,然后在值解包期间,对于 lhs 上的每个项目__setitem__,一个接一个地被调用。这意味着当arr[arr.index(...)]计算 lhs 时,第一个分配arr[i] = arr[arr.index(...)]已经发生,因此修改了数组。因此, lhsarr.index(...)不一定返回与 rhs 对应的相同值。

我们可以使用以下代码段来可视化这一点:

class Test:
    def __getitem__(self, x):
        print(f'get {x}')
        return x

    def __setitem__(self, x, y):
        print(f'set {x}={y}')


class Index:
    def __init__(self, x):
        self.x = x
        print(self)

    def __repr__(self):
        return f'{type(self).__name__}({self.x})'


test = Test()
test[Index(0)], test[Index(1)] = test[Index(2)], test[Index(3)]

哪个输出:

Index(2)
get Index(2)
Index(3)
get Index(3)
Index(0)
set Index(0)=Index(2)
Index(1)
set Index(1)=Index(3)

还要检查有关表达式评估顺序的文档

具体示例的更详细细分

对于您的具体示例,使用最小值的索引,这会导致无操作。这些是按顺序发生的步骤:

  1. Rhsarr.index(min(arr[i:]))被求值(它会是一个东西,我们称之为它m),然后arr[m]被求值(我们称之为它a;这是 的最小元素arr[i:])。
  2. Rhsarr[j]被评估(我们称之为b)。
  3. 评估Lhs arr.__setitem__(i, a),将索引处的元素设置为i最小值 ( a)。
  4. 评估Lhs arr.__setitem__(arr.index(min(arr[i:])), b)。由于之前的操作 (3.) 将arr.index(min(arr[i:]))i现在。由于这个原因,索引处的元素i被设置为b先前的元素arr[i],即它被设置回其先前的值,从而导致无操作。

相反,您可以预先计算索引:

index = arr.index(min(arr[i:]))
a[i], a[index] = a[index], a[i]

推荐阅读