空值与排序

2 投票
2 回答
524 浏览
提问于 2025-04-17 10:12

在处理NaN(不是一个数字)时,有可能会得到一个无法正确排序的列表:

--> NaN = float('nan')
--> spam = [1, 2, NaN, 3, NaN, 4, 5, 7, NaN]
--> sorted(spam)
[1, 2, nan, 3, nan, 4, 5, 7, nan]

我正在创建一个叫做Null的对象,它的行为和NaN很像。它的意思是,如果返回的对象是Null,那么它的实际值是未知的。这个Null对象也可以和其他任何类型的对象(比如intfloatstrbool等)进行互动,但无论怎么互动,结果都会是Null

从纯粹的角度来看,如果值是未知的,那么比较的结果也是未知的,因为实际值可能比正在比较的值大、小,或者相等。

从实际的角度来看,列表中到处都是Null对象会让人很头疼。

所以我倾向于让Null对象在比较时总是小于其他对象,这样它们就会一起排序。

当然,我也可以选择不解决这个问题,让用户自己实现自定义的排序规则。

你有什么想法、建议或批评吗?

2 个回答

5

NaN 通常被定义为“不是一个数字”,也就是说它和任何东西都无法比较。只要有计算涉及到 NaN,结果也会是 NaN

实际上:

>>> print float('nan') == float('nan')
False

没错:NaN 甚至和它自己都不相等。这种情况虽然有点反直觉,但其实是有原因的。主要原因可能是,与其他数字不同,NaN 没有一个“唯一”的排序方式。它应该排在第一、最后,还是在无穷大之前或之后呢?浮点数有一些奇怪的特性。不过,至少对于其他数字的排序是没有疑问的,比如 -infty < -123 < -0 <= +0 < 123 < +infty

既然它是“不是一个数字”,那它怎么能比其他数字大、小或相等呢?

当然,你可以定义一个自定义的比较函数,这样就能为 NaN 值提供明确的排序规则:

def s(x, y):
  import math
  if math.isnan(x): return 1
  return cmp(x, y)

注意我使用了 math.isnan。这个函数的意义很明确:它会先对所有数字进行排序,然后再处理任何 NaN 值。

1

如果Null对象有比较的行为,那么其他方法(比如索引)就会变得更复杂。想想看:

target = table.sql('select * where sales < 1000.00')

如果Null值被认为小于所有其他对象,那么target可能会有一些没有销售记录的行(这并不是我们想要的结果)。

所以,我觉得在这个问题上,实用性和纯粹性是朝着同一个方向走的:Null的比较结果是未知的。如果用户遇到Null值,他们需要自己决定该怎么处理。

撰写回答