何时使用hash()调用__eq__? [英] When does __eq__ gets called using hash()?
问题描述
如此处所述,
下面的代码,
class Person(object):
def __init__(self, name, ssn, address):
self.name = name
self.ssn = ssn
self.address = address
def __hash__(self):
print('in hash')
return hash(self.ssn)
def __eq__(self, other):
print('in eq')
return self.ssn == other.ssn
bob = Person('bob', '1111-222-333', None)
jim = Person('jim bo', '1111-222-333', 'sf bay area')
dmv_appointments = {}
print('calling hash')
dmv_appointments[bob] = 'tomorrow'
print('calling hash')
print(dmv_appointments[jim])
print('calling hash again')
print(dmv_appointments[bob])
输出:
Output:
calling hash
in hash
calling hash
in hash
in eq
tomorrow
calling hash again
in hash
tomorrow
问题:
为什么在访问jim
时调用__eq__
而不在bob
上调用__eq__
?
Why __eq__
gets called on accessing jim
but not on bob
?
推荐答案
简短答案:字典查找首先进行(廉价)引用相等性检查(x is y
)时,只有在该操作失败时才进行(费用更高)相等性检查(x == y
).
Short answer: a dictionary lookup first does a (cheap) reference equality check (x is y
) when searching a bucket, and only if that fails, a (more expensive) equality check (x == y
) is done.
__hash__
函数在内部不调用 __eq__
.如果构造了bob
和jim
,则不会调用任何此类方法.
The __hash__
function does not call __eq__
internally. Given you construct bob
and jim
, no such methods are called.
接下来,您将bob
与'tomorrow'
相关联.为了知道字典的哪个存储区,您必须存储bob
,您计算哈希值.现在,一旦完成,我们将存储bob
(以及值在正确的存储桶中).
Next you associate bob
with 'tomorrow'
. In order to know in which bucket of the dictionary, you have to store bob
, you calculate the hash. Now once you have done that we store bob
(and the value in the correct bucket).
接下来,我们要获取jim
.为了知道jim
存储在哪个存储区中,我们计算哈希值.接下来,我们开始在存储桶中搜索.值区将包含bob
.我们先执行参考检查(jim is bob
),但是失败了,因此我们回退了平等检查.该检查成功,因此我们返回与bob
相对应的值:'tomorrow'
.
Next we want to obtain jim
. In order to know in which bucket jim
resides, we calculate the hash. Next we start searching in the bucket. The bucket will contain bob
. We first perform a reference check (jim is bob
) but that fails, so then we fallback on the equality check. That check succeeds, so we return the value corresponding with bob
: 'tomorrow'
.
当我们要查找bob
时,会发生相同的情况:我们计算哈希,获取存储桶.在bob is bob
上执行参考检查,该检查成功.因此,我们不需要进行(可能更昂贵的相等性检查).我们只需返回值'tomorrow'
.
The same scenario happens when we want to look for bob
: we calculate the hash, fetch the bucket. Perform a reference check on bob is bob
, and that one succeeds. So we do not need a (probably more expensive equality check). We simply return the value 'tomorrow'
.
可以先通过以下(不健康的)代码来证明先进行参考检查的事实:
The fact that a reference check is done first can be proven with the following (unhealthy) code:
class Person(object):
def __init__(self, name, ssn, address):
self.name = name
self.ssn = ssn
self.address = address
def __hash__(self):
print('in hash')
return hash(self.ssn)
def __eq__(self, other):
print('in eq')
return False
在这里,我们始终返回False
以获得相等性.甚至:
Here we return always False
for equality. So even:
>>> bob == bob
in eq
False
>>> bob is bob
True
bob
不等于自身(这实际上不是一个好的设计,因为对于字典来说,一个对象等于其自身是一种契约:良好的平等关系是自反,对称和传递).但是,如果将bob
与'tomorrow'
关联,我们仍然能够获取与bob
关联的值:
bob
is not equal to itself (this is actually not good design, since for a dictionary, it is a contract that an object is equal to itself: a good equality relation is reflexive, symmetrical and transitive). Nevertheless, if we associate bob
with 'tomorrow'
, we are still able to fetch the value associated with bob
:
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
in hash
>>> dmv_appointments[bob]
in hash
'tomorrow'
这篇关于何时使用hash()调用__eq__?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!