max*_*max 12 python containers equality nan python-3.x
当我检查它在列表或集合中的存在时,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?
Run Code Online (Sandbox Code Playgroud)
请注意,如果我将一个用户定义的类的实例添加到a list,然后检查包含,__eq__则调用实例的方法(如果已定义) - 至少在CPython中.这就是为什么我认为list使用运算符测试包容==.
编辑:
每罗马的答案,这似乎是__contains__对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
Run Code Online (Sandbox Code Playgroud)
我说'奇怪',因为我没有在文档中看到它解释(也许我错过了),我认为这不应该作为一个实现选择.
当然,一个NaN对象可能与id另一个NaN对象不同(在某种意义上).(这并不奇怪; Python不保证这样的身份.实际上,我从未看到CPython共享在不同地方创建的NaN实例,即使它共享一个小数字或短字符串的实例.)这意味着在内置容器中测试NaN的存在是不确定的.
这非常危险,非常微妙.有人可能会运行我上面显示的代码,并错误地断定使用测试NaN成员资格是安全的in.
我不认为这个问题有一个完美的解决方法.一种非常安全的方法是确保NaN永远不会添加到内置容器中.(检查整个代码是很痛苦的......)
另一个选择是注意in左侧可能有NaN的情况,在这种情况下,使用分别测试NaN成员资格math.isnan().另外,还需要避免或重写其他操作(例如,设置交叉).
问题#1:当容器是相同的对象时,为什么会在容器中找到 NaN。
从文档中:
对于列表、元组、集合、frozenset、dict 或 collections.deque 等容器类型,表达式 x in y 等效于 any(x is e 或 x == e for e in y)。
这正是我在 NaN 中观察到的情况,所以一切都很好。为什么有这个规则?我怀疑这是因为dict/set想要诚实地报告它包含某个对象(如果该对象实际上位于其中)(即使__eq__()出于某种原因选择报告该对象不等于其自身)。
问题#2:为什么 NaN 的哈希值与 0 的哈希值相同?
从文档中:
由内置函数 hash() 调用,用于对哈希集合的成员(包括 set、frozenset 和 dict)进行操作。hash () 应该返回一个整数。唯一需要的属性是比较相等的对象具有相同的哈希值;建议以某种方式将对象组件的散列值混合在一起(例如使用异或),这些组件也在对象比较中发挥作用。
请注意,该要求仅在一个方向;具有相同哈希值的对象不必相等!起初我以为这是一个错字,但后来我意识到事实并非如此。无论如何,即使使用默认值,哈希冲突也会发生(请参阅此处__hash__()的出色解释)。容器可以毫无问题地处理碰撞。当然,它们最终确实使用运算符来比较元素,因此它们很容易得到多个 NaN 值,只要它们不相同!尝试这个:==
>>> nan1 = float('nan')
>>> nan2 = float('nan')
>>> d = {}
>>> d[nan1] = 1
>>> d[nan2] = 2
>>> d[nan1]
1
>>> d[nan2]
2
Run Code Online (Sandbox Code Playgroud)
所以一切都按记录进行。但是……这非常非常危险!有多少人知道 NaN 的多个值可以在一个字典中并存?有多少人会觉得这很容易调试?
float我建议将 NaN 设为不支持散列的子类的实例,因此不会意外添加到set/中dict。我会将其提交给 python-ideas。
最后,我在这里的文档中发现了一个错误:
__contains__()对于不定义但定义了的用户定义类__iter__(),如果在迭代 时生成某个x in y值,则为 true 。如果在迭代期间引发异常,就好像引发了该异常。zx == zyin最后,尝试旧式迭代协议:如果类定义
__getitem__(),x in y则当且仅当存在非负整数索引使得i、x == y[i]且所有较低整数索引不会引发IndexError异常时, 为 true。(如果引发任何其他异常,则就像in引发该异常一样)。
is您可能会注意到,与内置容器不同,这里没有提及。我对此感到惊讶,所以我尝试了:
>>> nan1 = float('nan')
>>> nan2 = float('nan')
>>> class Cont:
... def __iter__(self):
... yield nan1
...
>>> c = Cont()
>>> nan1 in c
True
>>> nan2 in c
False
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,首先检查身份==- 与内置容器一致。我将提交一份报告来修复文档。
| 归档时间: |
|
| 查看次数: |
2835 次 |
| 最近记录: |