__hash__
将永远被调用;__eq__
如果对象确实在字典中,或者具有相同哈希的另一个对象在字典中,将被调用。哈希值用于缩小可能键的选择范围。键按哈希值分组到“桶”中,但是对于查找,Python 仍然必须检查桶中的每个键是否与查找键相等。请参阅http://wiki.python.org/moin/DictionaryKeys。看看这些例子:
>>> class Foo(object):
... def __init__(self, x):
... self.x = x
...
... def __hash__(self):
... print "Hash"
... return hash(self.x)
...
... def __eq__(self, other):
... print "Eq"
... return self.x == other.x
>>> Foo(1) in d
Hash
Eq
10: True
>>> Foo(2) in d
Hash
Eq
11: True
>>> Foo(3) in d
Hash
Eq
12: True
>>> Foo(4) in d
Hash
13: False
在那个例子中,你可以看到__hash__
总是被调用。 __eq__
当对象在字典中时,每次查找都会调用一次,因为它们都有不同的哈希值,所以一次相等性检查足以验证具有该哈希值的对象确实是被查询的对象。 __eq__
在最后一种情况下没有调用,因为 dict 中没有一个对象具有与 相同的哈希值Foo(4)
,因此 Python 不需要继续使用__eq__
.
>>> class Foo(object):
... def __init__(self, x):
... self.x = x
...
... def __hash__(self):
... print "Hash"
... return 1
...
... def __eq__(self, other):
... print "Eq"
... return self.x == other.x
>>> d = {Foo(1): 2, Foo(2): 3, Foo(3): 4}
Hash
Hash
Eq
Hash
Eq
Eq
>>> Foo(1) in d
Hash
Eq
18: True
>>> Foo(2) in d
Hash
Eq
Eq
19: True
>>> Foo(3) in d
Hash
Eq
Eq
Eq
20: True
>>> Foo(4) in d
Hash
Eq
Eq
Eq
21: False
在这个版本中,所有对象都具有相同的哈希值。在这种情况下__eq__
总是被调用,有时会被多次调用,因为哈希不区分值,所以 Python 需要显式检查字典中所有值的相等性,直到找到相等的值(或发现它们都不等于它正在寻找的一个)。有时它会在第一次尝试时找到它(Foo(1) in dict
上面),有时它必须检查所有值。