Python:max/min内置函数取决于参数顺序

58

max(float('nan'), 1) 的结果是 nan

max(1, float('nan')) 的结果是 1

这是预期行为吗?


感谢答案。

max 在可迭代对象为空时会引发异常。那么为什么Python的 max 在存在 nan 时不会引发异常呢?或者至少做些有用的事情,比如返回 nan 或忽略 nan。当前行为非常不安全,似乎完全没有道理。

我发现了这种行为的一个更令人惊讶的后果,所以我刚刚发布了一个相关问题


3
@khachik说:“我只是想说max函数的结果取决于参数的顺序,这有点出乎意料,即使它只在一个例子中发生。但实际上,在你的例子中也是有效的:max(float('nan'), 1, 0.5)返回nan。” - max
1
错误不在于max函数。问题在于你正在使用浮点数,并假设它们具有任何有意义的数学行为。 - Antimony
4
@Antimony:我不认为浮点数值完美地代表数学对象。但我认为它们在构建软件方面很有用,这要求它们的行为符合大多数经验丰富的开发人员的期望,任何违反这个假设的都是不好的设计或者是一个错误。 - max
1
请注意,当您比较相等但不同的对象时,会出现类似的行为。例如,1和1.0的最大值取决于顺序。 - zondo
@zondo 看起来很合理。例如,我们假设两个字符串字面值abcabc实际上不是同一个对象(请注意,在某些实现中,它们实际上可能是同一个对象,让我们忽略这一点)。当然,max('abc','abc')将返回其中一个字面值,但是返回哪一个则是任意的且取决于顺序。完全如你所料,我想? - max
显示剩余4条评论
3个回答

50
In [19]: 1>float('nan')
Out[19]: False

In [20]: float('nan')>1
Out[20]: False

nan 是一个浮点数,既不大于也不小于整数 1max 从选择第一个元素开始,仅在找到比当前元素严格更大的元素时才进行替换。

In [31]: max(1,float('nan'))
Out[31]: 1

由于nan不大于1,因此返回1。

In [32]: max(float('nan'),1)
Out[32]: nan

由于1不比nan大,所以返回nan


PS. 注意,np.maxfloat('nan')有不同的处理方式:

In [36]: import numpy as np
In [91]: np.max([1,float('nan')])
Out[91]: nan

In [92]: np.max([float('nan'),1])
Out[92]: nan

但是如果你希望忽略 np.nan,你可以使用 np.nanmax

In [93]: np.nanmax([1,float('nan')])
Out[93]: 1.0

In [94]: np.nanmax([float('nan'),1])
Out[94]: 1.0

2
@javadba IEEE-754规则之一关于NaN的规定是:“当一个或两个操作数为NaN时,比较EQ、GT、GE、LT和LE返回FALSE”。因此,它实际上是“所有实现浮点数的编程语言”,而不仅仅是“Python...”。 - user3064538

9

我以前没有见过这个,但是它很有道理。请注意,nan 是一个非常奇怪的对象:

>>> x = float('nan')
>>> x == x
False
>>> x > 1
False
>>> x < 1
False

我认为在这种情况下,max的行为是未定义的——你期望得到什么答案?唯一明智的行为是假定操作是反对称的。


请注意,您可以通过创建一个破损的类来复制此行为:

>>> class Broken(object):
...     __le__ = __ge__ = __eq__ = __lt__ = __gt__ = __ne__ =
...     lambda self, other: False
...
>>> x = Broken()
>>> x == x
False
>>> x < 1
False
>>> x > 1
False
>>> max(x, 1)
<__main__.Broken object at 0x024B5B50>
>>> max(1, x)
1

对于 NaN 的比较,您应该使用 math.isnan 函数。 - Andrew

1

Max的工作方式如下:

首先将第一个项目设置为maxval,然后将其与下一个项目进行比较。比较将始终返回False:

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

所以如果第一个值是nan,那么(由于比较返回false),它在下一步中不会被替换。

另一方面,如果1是第一个值,同样的情况也会发生:但在这种情况下,由于1已经被设置,它将成为最大值。

您可以在Python代码中验证此内容,只需查找Python/bltinmodule.c中的min_max函数即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接