Python sorted() 的 key 函数奇怪行为

2 投票
3 回答
13533 浏览
提问于 2025-04-16 06:41

在我调试一些不太合理的行为时,我发现了Python 2.5中sorted()函数调用的一些奇怪现象:

>>> aa = [10, 5, 20]
>>> sorted(range(len(aa)))
[0, 1, 2]
sorted(range(len(aa)), key=lambda a: aa[a])
[1, 0, 2]
sorted(range(len(aa)), key=lambda a: -aa[a])
[2, 0, 1]

前两个调用的结果是我预期的,但最后一个结果在我看来简直是错误的!它应该是:[1, 2, 0]。

为了进一步探究这个问题的根源,我进行了更多实验,发现了这个(虽然没有使用lambda或取反操作,但问题本质上是一样的):

>>> bb = [-10, -5, -20]
>>> sorted([0, 1, 2], key=bb.__getitem__)
[2, 0, 1]

甚至像下面这样的代码也不奏效,显示出双重取反又一次出现了问题:

>>> bb = [-10, -5, -20]
>>> def fun(i):
...    return bb[i]
>>> sorted([0, 1, 2], key=fun)
[2, 0, 1]
>>> def fun2(i):
...     return -bb[i]
>>> sorted([0, 1, 2], key=fun2)
[1, 0, 2]

我是不是快要疯了,问题出在哪里?或者为什么Python 3.x没有以前很好用的cmp参数(我不使用它的原因是为了兼容性)?

3 个回答

0

这对我来说很有道理

>>> bb = [-10, -5, -20]
>>> sorted([0, 1, 2], key=bb.__getitem__)
[2, 0, 1]  ==> corresponds to bb.__getitem__ of [-20, -10, -5]
1

最后一个我觉得简单来说就是错了!应该是:[1, 2, 0]

在你的第二个例子中,关键部分是lambda a: aa[a],这个可以让你得到元素按大小升序排列的索引。

在最后一个例子中,关键部分是lambda a: -aa[a]。因为有个负号,这样可以让你得到元素按大小降序排列的索引。

所以最后的结果应该是[2, 0, 1],这是[1, 0, 2]的反向。

在这个例子中

>>> bb = [-10, -5, -20]
>>> sorted([0, 1, 2], key=bb.__getitem__)
[2, 0, 1]

你得到的是元素按大小升序排列的索引——[2, 0, 1]对应的是[-20, -10, -5]

在你最后的两个例子中,你再次得到了元素按大小升序排列的索引([2, 0, 1]),或者按大小降序排列的索引([1, 0, 2])。

8

这个函数返回的值就像是一个代理,用来代表我们要排序的值。

所以当你写

sorted(range(len(aa)), key=lambda a: -aa[a])

你其实是在排序 range(len(aa),也就是 [0, 1, 2],但用的是这些值 -aa[0], -aa[1], -aa[2] 作为代理值。

range(len(aa))   0   1   2    <-- values
aa[a]           10   5  20
-aa[a]         -10  -5 -20    <-- proxy values

因为 -20,也就是 -aa[2],是最小的代理值,所以它对应的值 2 就成了排序结果中的第一个元素。

接下来 -10,也就是 -aa[0] 是第二小的,它对应的值 0 就成了排序结果中的第二个元素。

最后 -5,也就是 -aa[1] 是最后一个值,所以 1 就是排序结果中的最后一个数字。

因此,sorted(range(len(aa)), key=lambda a: -aa[a]) 的结果是 [2, 0, 1]

所以,Python 给出的答案是正确的。

撰写回答