我有一个实现myClass
和__hash__
的课程(我们称之为__eq__
)。我还有dict
将myClass
个对象映射到某个值,计算需要一些时间。
在我的程序过程中,许多(大约数百万)myClass
个对象被实例化。这就是我使用dict
来跟踪这些值的原因。
但是,有时新的myClass
对象可能等同于旧的__eq__
方法(由myClass
方法定义)。因此,我不想再次计算该对象的值,而只是在dict
中查找旧if myNewMyClassObj in dict
对象的值。要做到这一点,我做in
。
这是我的问题:
当我使用__hash__
子句时,会调用什么,__eq__
或dict
?使用__hash__
的关键是它是O(1)查找时间。所以必须调用__hash__
。但是如果__eq__
和if myNewMyClassObj in dict
不是等价的方法怎么办?在这种情况下,我会对dict
产生误报吗?
跟进问题:
我希望尽量减少myClass
中的条目数,因此我最好只保留dict
中一组等效__eq__
个对象中的一个。同样,在计算if myNewClassObj in dict
时,似乎需要调用dict
,这会将{{1}}的O(1)查找时间玷污为O(n)查找时间
答案 0 :(得分:8)
首先,调用__hash__(myNewMyClassObj)
。如果在字典中找不到具有相同散列的对象,则Python假定myNewMyClassObj
不在字典中。 (请注意,Python要求每当__eq__
对两个对象求值相等时,它们的__hash__
必须相同。)
如果在字典中找到了具有相同__hash__
的某些对象,则会在每个对象上调用__eq__
。如果__eq__
对其中任何一个评估为相等,则myNewMyClassObj in dict_
将返回True。
因此,您只需要确保__eq__
和__hash__
都很快。
对于您的后续问题:是的,dict_
只存储一组等效MyClass
对象中的一个(由__eq__
定义)。 (按照设定。)
请注意,__eq__
仅在具有相同哈希值并分配给同一个存储桶的对象上调用。这些对象的数量通常是一个非常小的数字(dict
实现确保这一点)。所以你仍然有(大致)O(1)
查询性能。
答案 1 :(得分:7)
__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__
。当对象在dict中时,每次查找都会调用__eq__
,因为它们都具有不同的哈希值,因此一次等式检查足以验证具有该哈希值的对象确实是被查询的对象。在最后一种情况下不会调用__eq__
,因为dict中没有任何对象具有与Foo(4)
相同的哈希值,因此Python不需要继续使用__eq__
。 / p>
>>> 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需要显式检查dict中所有值的相等性,直到它找到相等的值(或找到它们都不等于它正在寻找的那个)。有时它会在第一次尝试时找到它(Foo(1) in dict
以上),有时它必须检查所有值。
答案 2 :(得分:1)
__ hash__定义了放置对象的存储区,只有当对象位于同一存储桶中时才会调用__eq__。