空值与排序
在处理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
对象也可以和其他任何类型的对象(比如int
,float
,str
,bool
等)进行互动,但无论怎么互动,结果都会是Null
。
从纯粹的角度来看,如果值是未知的,那么比较的结果也是未知的,因为实际值可能比正在比较的值大、小,或者相等。
从实际的角度来看,列表中到处都是Null对象会让人很头疼。
所以我倾向于让Null对象在比较时总是小于其他对象,这样它们就会一起排序。
当然,我也可以选择不解决这个问题,让用户自己实现自定义的排序规则。
你有什么想法、建议或批评吗?
2 个回答
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
值。
如果Null
对象有比较的行为,那么其他方法(比如索引)就会变得更复杂。想想看:
target = table.sql('select * where sales < 1000.00')
如果Null
值被认为小于所有其他对象,那么target
可能会有一些没有销售记录的行(这并不是我们想要的结果)。
所以,我觉得在这个问题上,实用性和纯粹性是朝着同一个方向走的:Null
的比较结果是未知的。如果用户遇到Null
值,他们需要自己决定该怎么处理。