Python字典中__hash__和__eq__的计算顺序是怎样的?

5

我正在尝试理解Python字典在内部查找键时必须执行的操作。 我觉得首先会计算哈希值,如果发生冲突,Python会迭代遍历键,直到找到一个使等于返回True的键。 这让我想知道为什么以下代码能够运行(仅用于理解内部操作):

class MyClass(object):
    def __eq__(self, other):
        return False

    def __hash__(self):
        return 42

if __name__=='__main__':

    o1 = MyClass()
    o2 = MyClass()
    d = {o1: 'o1', o2: 'o2'}
    assert(o1 in d)      # 1
    assert(d[o1]=='o1')  # 2
    assert(o2 in d)      # 3
    assert(d[o2]=='o2')  # 4

在情况#2和#4中,字典应该无法找到正确的键(返回'o1'或'o2'其中之一,或根据内部实现抛出错误)。由于eq返回False,它如何能够在两种情况下都找到正确的键呢。

我看过的所有哈希文档都提到了hasheq,而没有提到cmpne等,这使我认为这两个是在该场景中发挥作用的唯一因素。

1个回答

5

您用作字典键的任何内容都必须满足不变式:bool(x == x) is True。(我本可以只说x == x,但有些对象甚至不是布尔值。)

字典假定这将保持不变,因此它用于检查键等价性的例程实际上首先检查对象标识,然后再使用==。这种预先检查是实现细节;您不应依赖它是否发生。

对于(x == x)不为True的合理对象包括float('nan')numpy.array([1, 2])


所以在他的例子中,o1 == o1 会返回 False,但是 id(o1) == id(o1) 会返回 True? - Timur Ridjanovic
我刚试了一下,o1 == o1 返回 False,而 id(o1) == id(o1) 返回 True。 - Timur Ridjanovic
@TimurRidjanovic:是的。is 操作符是比较对象身份更安全、有时也更高效的方法。 - user2357112

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