NaNs是词典的关键

时间:2011-06-22 14:47:44

标签: python numpy nan

有人可以向我解释以下行为吗?

>>> import numpy as np
>>> {np.nan: 5}[np.nan]
5
>>> {float64(np.nan): 5}[float64(np.nan)]
KeyError: nan

为什么它在第一种情况下起作用,而在第二种情况下不起作用? 此外,我发现以下内容有效:

>>> a ={a: 5}[a]
float64(np.nan)

2 个答案:

答案 0 :(得分:33)

这里的问题是NaN不等于自身,如IEEE标准中对浮点数的定义:

>>> float("nan") == float("nan")
False

当字典查找某个键时,它大致会这样做:

  1. 计算要查找的密钥的哈希值。

  2. 对于具有相同散列的dict中的每个键,检查它是否与要查找的键匹配。这项检查包括

    一个。检查对象标识:如果字典中的密钥和要查找的密钥与is运算符指示的对象相同,则找到密钥。

    湾如果第一次检查失败,请使用__eq__运算符检查是否相等。

  3. 第一个例子成功,因为np.nannp.nan是同一个对象,所以它们无法比较相等并不重要:

    >>> numpy.nan is numpy.nan
    True
    

    在第二种情况下,np.float64(np.nan)np.float64(np.nan)不是同一个对象 - 两个构造函数调用创建两个不同的对象:

    >>> numpy.float64(numpy.nan) is numpy.float64(numpy.nan)
    False
    

    由于对象也没有比较相等,因此字典断定找不到密钥并抛出KeyError

    你甚至可以这样做:

    >>> a = float("nan")
    >>> b = float("nan")
    >>> {a: 1, b: 2}
    {nan: 1, nan: 2}
    

    总之,将NaN作为字典键避免使用似乎更为明智。

答案 1 :(得分:2)

请注意,在Python 3.6中不再是这种情况了:

>>> d = float("nan") #object nan
>>> d
nan
>>> c = {"a": 3, d: 4}
>>> c["a"]
3
>>> c[d]
4

据我所知:

c是一个字典,包含3个与" a"相关联的字典。和4相关联。 Python 3.6内部查找字典的方式已经改变,现在它比较了两个指针,如果它们指向同一个对象,则认为保留了相等性。否则他们比较哈希,如果哈希不同,那么它不是同一个对象。毕竟,如果仍然需要,键是"手动"比较。

这意味着虽然IEEE754规定NAN不等于它自己:

>>> d == d
False

首先查找字典时会考虑指针,并且因为它们指向相同的nan对象,所以返回4。

另请注意:

>>> e = float("nan")
>>> e == d
False
>>> c[e]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: nan
>>> c[d]
4

所以不是每个nan都指向4,所以保留了某种IEEE754。这是因为尊重nan永远不等于自己的标准会降低效率,而不是忽视标准。正是因为您在字典中存储了以前版本中无法再访问的内容。