Python sorted() 的 key 函数奇怪行为
在我调试一些不太合理的行为时,我发现了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 个回答
这对我来说很有道理
>>> bb = [-10, -5, -20]
>>> sorted([0, 1, 2], key=bb.__getitem__)
[2, 0, 1] ==> corresponds to bb.__getitem__ of [-20, -10, -5]
最后一个我觉得简单来说就是错了!应该是:[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]
)。
这个函数返回的值就像是一个代理,用来代表我们要排序的值。
所以当你写
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 给出的答案是正确的。