【问题标题】:Why doesn't NaN raise any errors in python?为什么 NaN 不会在 python 中引发任何错误?
【发布时间】:2021-11-18 12:18:57
【问题描述】:

在我看来,float('nan') 之类的东西应该被优化,但显然它们不在 Python 中。

>>> NaN = float('nan')
>>> a = [ 1, 2, 3, NaN ]
>>> NaN in a
True
>>> float('nan') in a
False

不优化nan 有什么意义吗? 在我看来,nan 只是nan

除此之外,当你在这些东西上使用sorted 时,它们会给出奇怪的结果:

>>> sorted([3, nan, 4, 2, nan, 1])
[3, nan, 1, 2, 4, nan]


>>> 3 > float('nan')
False
>>> 3 < float('nan')
False

nan 上的比较是这样定义的,但对我来说似乎不是“pythonic”。为什么不报错?

【问题讨论】:

  • 我喜欢这个观察,我也觉得它很混乱。 7 个语句中的哪一个应该引发异常?
  • 在 C# 中,结果是 [NaN, NaN, 1,2,3,4],恕我直言,这更像是 pythonic。
  • 简单的答案是“因为这是 IEEE754 定义它的原因,至少在定义它的时候,至少对定义标准的人来说是有意义的”。
  • NaN 的一个有趣特性是它打破了“(a is b) 隐含 (a==b)”协议,in 运算符在检查相等性之前检查身份。这意味着x in somelist 可以与all(x != y for y in somelist) 同时为真。
  • “优化”是什么意思?

标签: python


【解决方案1】:

会员测试

float('nan') 的两个不同实例彼此不相等。它们是“不是数字”,因此它们不应该也必须相等是有道理的。它们是不是数字的对象的不同实例:

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

据记录 here:

对于容器类型,例如 list、tuple、set、frozenset、dict 或 collections.deque, y 中的表达式 x 等价于 any(x is e 或 x == e for e in y)。

有一个身份检查!这就是为什么您在问题中看到这种行为以及为什么 NaN in a 返回 Truefloat('nan') in a 没有。


在 Python 中排序

Python 将Timsort 算法用于其sorted() 函数。 (另请参阅this 以获得文字解释。)我不打算深入讨论。我只是想演示一个简单的例子:

这是我的班级A。这将是我们的float('nan') 对象。它的作用类似于float('nan'),因为它为所有比较操作返回False

class A:
    def __init__(self, n):
        self.n = n

    def __lt__(self, other):
        print(self, 'lt is calling', other)
        return False

    def __gt__(self, other):
        print(self, 'gt is calling', other)
        return False

    def __repr__(self):
        return f'A({self.n})'

class B:
    def __init__(self, n):
        self.n = n

    def __lt__(self, other):
        print(self, 'lt is calling', other)
        return False

    def __gt__(self, other):
        print(self, 'gt is calling', other)
        return False

    def __repr__(self):
        return f'B({self.n})'

当我们使用没有reverse=True 参数的sorted() 函数(或list.sort() 方法)时,我们要求迭代按升序排序。为此,Python 尝试依次调用__lt__ 方法,从列表中的第二个对象开始,查看它是否小于其前一个对象,依此类推:

lst = [A(1), B(2), A(3), B(4)]
print(sorted(lst))

输出:

B(2) lt is calling A(1)
A(3) lt is calling B(2)
B(4) lt is calling A(3)
[A(1), B(2), A(3), B(4)]

现在,切换回您的示例:

lst = [3, A(1), 4, 2, A(1), 1]
print(sorted(lst))

输出:

A(1) lt is calling 3
A(1) gt is calling 4
A(1) gt is calling 2
A(1) lt is calling 2
A(1) lt is calling 4
A(1) gt is calling 1
[3, A(1), 1, 2, 4, A(1)]
  1. A(1).__lt__(3) 将返回 False。这意味着A(1) 不少于 大于 3 或这意味着 3 相对于 A(1) 处于正确位置。
  2. 然后这里int.__lt__(4, A(1)) 被调用,因为它返回 NotImplemented 对象,Python 检查 A(1) 是否有 实现__gt__ 是的,所以A(1).__gt__(4) 将返回 False 再次,这意味着 A(1) 对象在正确的位置 相对于4
  3. (等)

这就是为什么sorted() 的结果看起来很奇怪,但它是可以预测的。 A(1) 在这两种情况下都是对象,我的意思是当 int 类返回 NotImplemented 以及从 A(1) 调用 __lt__ 时,返回 False。

最好检查Timsort 算法并考虑这些点。如果我仔细阅读 Timsort 算法,我会包括剩余的步骤。

【讨论】:

  • @don'ttalkjustcode 谢谢,更正并更新了更好的例子。
  • 是的,B 更好。也可以使 B 表现得像一个 int,即,与其他 B 对象正常比较并返回 NotImplemented 和 A 对象。然后我们会在输出中看到int.__lt__(4, A(1)) 调用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-09
  • 2016-11-07
  • 1970-01-01
相关资源
最近更新 更多