【发布时间】:2012-04-11 21:06:13
【问题描述】:
当我检查它在列表或集合中的存在时,NaN 得到了完美的处理。但我不明白怎么做。 [更新:不,不是;如果找到相同的 NaN 实例,则报告为存在;如果只找到不同的 NaN 实例,则报告为不存在。]
我认为列表中的存在是通过相等性来测试的,所以我预计不会找到 NaN,因为 NaN != NaN。
hash(NaN) 和 hash(0) 都是 0。字典和集合如何区分 NaN 和 0?
使用
in运算符检查任意容器中是否存在 NaN 是否安全?还是依赖于实现?
我的问题是关于 Python 3.2.1;但如果未来版本中存在/计划有任何更改,我也想知道。
NaN = float('nan')
print(NaN != NaN) # True
print(NaN == NaN) # False
list_ = (1, 2, NaN)
print(NaN in list_) # True; works fine but how?
set_ = {1, 2, NaN}
print(NaN in set_) # True; hash(NaN) is some fixed integer, so no surprise here
print(hash(0)) # 0
print(hash(NaN)) # 0
set_ = {1, 2, 0}
print(NaN in set_) # False; works fine, but how?
请注意,如果我将用户定义类的实例添加到list,然后检查是否包含,则调用实例的__eq__ 方法(如果已定义)- 至少在 CPython 中是这样。这就是为什么我假设 list 包含使用运算符 == 进行测试。
编辑:
根据 Roman 的回答,__contains__ for list、tuple、set、dict 的行为似乎非常奇怪:
def __contains__(self, x):
for element in self:
if x is element:
return True
if x == element:
return True
return False
我说“奇怪”是因为我没有在文档中看到它的解释(也许我错过了),我认为这不应该作为实现选择。
当然,一个 NaN 对象可能与另一个 NaN 对象不同(在id 的意义上)。 (这并不奇怪;Python 不保证这样的身份。事实上,我从未见过 CPython 共享在不同位置创建的 NaN 实例,即使它共享一个小数字或短字符串的实例。)这意味着在内置容器中测试 NaN 的存在是未定义的。
这是非常危险的,也是非常微妙的。有人可能会运行我上面显示的代码,并错误地得出使用 in 测试 NaN 成员资格是安全的结论。
我认为这个问题没有完美的解决方法。一种非常安全的方法是确保永远不会将 NaN 添加到内置容器中。 (检查整个代码很痛苦......)
另一种选择是注意in 左侧可能有NaN 的情况,在这种情况下,使用math.isnan() 单独测试NaN 成员资格。此外,其他操作(例如,设置交集)也需要避免或重写。
【问题讨论】:
-
底线:为了安全起见使用:any(math.isnan(element) for element in list_)
-
@jsbueno: 是的......但这对设置交集问题没有帮助;它也不处理
for x in cont1: if x in cont2 do something的情况...我想说的底线是“非常害怕,只是希望你不要忽视一些事情” -
这无济于事-您必须同意没有简单的解决方案。您可以使用上述循环将任何 NaN 转换为读取“NaN”的字符串 - 这些将明确比较。
标签: python python-3.x containers equality nan