Python的dict.get(k)方法返回none,尽管键存在

3
也许我的Python字典理解不够好,但问题在于:在字典“eggs”中是否存在一对{yolk:shell},但是eggs.get(yolk)会返回None吗?
因此,在大型代码中,我对字典执行多个get操作,并在某些迭代后观察到这种情况。
   >>> for key, value in nodehashes.items():
   ...    print(key, nodehashes.get(key), value)
   ............................
   ...........................
   <Graph.Node object at 0x00000264128C4DA0> 3309678211443697093 3309678211443697093
   <Graph.Node object at 0x00000264128C4DD8> 3554035049990170053 3554035049990170053
   <Graph.Node object at 0x00000264128C4E10> None -7182124040890112571  # Look at this!!
   <Graph.Node object at 0x00000264128C4E48> 3268020121048950213 3268020121048950213
   <Graph.Node object at 0x00000264128C4E80> -1243862058694105659 -1243862058694105659
   ............................
   ............................


乍一看,好像在代码中某处删除了键值,但是 nodehashes.items() 如何返回正确的键值对?我检查了整个区域,没有弹出任何项。这怎么可能发生?

我知道我没有发布示例是我的错,但我真的不知道从代码哪里开始查找。节点在开始时被散列,并且只能使用get访问。令人惊讶的是,即使是 PyCharm 的调试器也显示存在键值对。但是 get 返回 None。所以如果有人之前遇到过这个问题,我很愿意听取建议。

def __eq__(self, other): 
    if (self.x == other.x) and (self.y == other.y): 
        return True 
    else: 
        return False 

def __hash__(self): 
    return hash(tuple([self.x, self.y]))

2
这是并发代码吗?还有另一个线程/...可能会在您迭代时修改nodehashes吗? - AKX
1
class Node 定义了 __hash__ 和/或 __eq__ 吗? - juanpa.arrivillaga
@juanpa.arrivillaga 是的,Node类定义了以下两个函数:def __eq__(self, other): if (self.x == other.x) and (self.y == other.y): return True else: return False def __hash__(self): return hash(tuple([self.x, self.y])) - Jaswant P
首先,为什么要重写__eq____hash__方法呢? - AKX
@AKX。__eq__用于检查两个节点在x,y方面是否相等。接下来是__hash__。在另一个类中,我使用{nodehash,index}构建了一个字典,其中nodehashhash(x,y),而index是顶点缓冲区对象(https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Buffer_Object)中位置和颜色的索引。因此,我认为按位置哈希将是标识节点的唯一方法,但事实证明这是愚蠢的设计模式,我可以重写__hash__以返回对象的ID。 - Jaswant P
显示剩余5条评论
1个回答

3
如果您在可变对象上有自定义的__hash__方法,那么就可以重现这个问题:
class A:
    def __hash__(self):
        return hash(self.a)

>>> a1 = A()
>>> a2 = A()
>>> a1.a = 1
>>> a2.a = 2
>>> d = {a1: 1, a2: 2}
>>> a1.a = 3
>>> d.items()
dict_items([(<__main__.A object at 0x7f1762a8b668>, 1), (<__main__.A object at 0x7f17623d76d8>, 2)])
>>> d.get(a1)
None

你可以看到,d.items() 仍然可以访问两个 A 对象,但是 get 找不到它们了,因为 hash 值已经改变。


@juanpa nan永远不会等于nan,因此节点永远不会被找到。更有可能的是使用可变属性作为哈希的经典错误。 Python的一个缺点是它不能轻易地警告您这些缺点,在C#中,这将是静态编译器警告。 - Voo
1
@JaswantP,你可以在字典中使用 id(node) 作为键。 - user2390182
@juanpa.arrivillaga 这是不可能的,程序允许用户移动节点,因此它们应该是可变的。 - Jaswant P
@schwobaseggl,那就意味着对象ID对吧?那太完美了。 - Jaswant P
1
@JaswantP 是的,id将保持不变。但我忘记了,如果你定义了一个__eq__ 而没有定义 __hash__ 那么 __hash__ 就会被隐式设置为 None,当你尝试进行哈希时就会引发类型错误。所以你可以使用 __hash__ = object.__hash__ 或者自己定义它只返回 id(self) - juanpa.arrivillaga
显示剩余6条评论

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