含NaN时排序函数失效却存在例外的原因解析
这事儿核心就在于NaN的比较特性和Python排序算法对全序关系的依赖,咱们一步步拆解:
为什么第一个示例排序效果不佳?
Python的默认排序(比如list.sort()或sorted())依赖于全序比较规则——简单说就是对任意两个元素a和b,必须能明确判断出a < b、b < a或者a == b三者中的一个(也就是三歧性)。
但NaN天生是个“刺头”:它和任何值(包括它自己)的比较都会返回False。比如:
float('nan') < 10→False10 < float('nan')→Falsefloat('nan') == float('nan')→False
当你排序包含NaN的元组列表时,比如[(float('nan'), 2), (1, 3)],算法会先比较元组的第一个元素。这时候因为NaN和1的比较既不满足小于也不满足大于,排序算法就懵了——它无法确定这两个元组的相对位置,最终的排序结果就会不可预测(比如保留原顺序、出现混乱排列),这就是你看到的“效果不佳”。
为什么第二个示例能正常排序?
你微调后的代码,肯定是给NaN指定了明确的排序优先级,修复了全序关系的问题。最常见的做法是用key函数(Python 3推荐的方式),把NaN映射到一个极端值,让算法能明确它的位置:
举个实际的例子,假设你写了这样的key函数:
import math lst = [(float('nan'), 2), (1, 3), (float('nan'), 1)] # 把NaN映射到正无穷,这样所有含NaN的元组会被放到最后 lst.sort(key=lambda t: (t[0] if not math.isnan(t[0]) else float('inf'), t[1]))
这时候每个元组的key值都满足全序关系了:正常数值按大小排,NaN的key是inf,肯定比所有正常数值都大,排序时就会乖乖待在最后。
如果你是用了自定义比较器(比如通过functools.cmp_to_key),本质也是一样的:手动定义了NaN和其他值的比较规则,让算法能明确它们的相对位置。
一句话总结
两个示例的核心差异就是:是否给NaN提供了明确的排序依据。默认排序无法处理NaN的特殊比较逻辑,而自定义key或比较器补上了这个规则,让排序算法能正常工作。
内容的提问来源于stack exchange,提问作者Håken Lid

