2011-06-22 29 views
15

任何人都可以向我解释以下行为吗?NaNs作为词典的关键

>>> 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) 
+0

这将永远是真的:'float('nan')!= float('nan')' – JBernardo

回答

27

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

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

当字典查找关键字,它大致这样做:

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

  2. 对于带相同散列的字典中的每个键,检查它是否与要查找的键匹配。此项检查包括:

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

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

第一个例子成功,因为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

ops ...没有检查它的正确性:) – JBernardo

+3

最后的声明值得更多的重点。 – job

+0

是否有保证所有'float('nan')'具有相同的内存位置,即float('nan')'是单例?没有它,即使使用普通的'float('nan')'也不是一个好主意。同样的问题关于'np.nan'。 – max

1

请注意,这是不再在Python 3.6的情况下:

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

据我了解:

d =物体楠 c是相关联的,它包含一个字典3 “a” 和4与nan关联,但是,由于Python 3.6内部在字典中查找的方式发生了变化,现在它比较了两个指针,并且如果它们指向同一个对象,则认为它们保持相等。

这意味着,虽然:

>>> d == d 
False 

因为IEEE754是如何指定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 

因此不是每个楠指向4,所以某种IEEE754的被保留。这是因为尊重nan不等于自身的标准会降低效率而不是忽视标准。正是因为你在字典中存储了一些你以前版本无法访问的东西。