我有一个类(姑且称之为myClass
同时实现) __hash__
和__eq__
。 我也有一个dict
映射myClass
对象为某个值,计算这需要一些时间。
在我的程序的过程中,许多(在百万量级) myClass
对象实例化。 这就是为什么我用dict
来跟踪这些值。
然而,有时新的myClass
对象可能是等同于一个较旧的酮(如通过定义__eq__
方法)。 因此,而不是再次计算该对象的值,我宁愿只是查找旧的价值myClass
对象的dict
。 要做到这一点,我做的if myNewMyClassObj in dict
。
我的问题是:
当我使用in
条款,什么被调用, __hash__
或__eq__
? 使用的点dict
是,它是O(1)查找时间。 所以后来__hash__
必须被调用。 但是,如果__hash__
和__eq__
是不等价的方法呢? 在这种情况下,我会得到一个误报为if myNewMyClassObj in dict
?
跟进的问题:
我要尽量减少我的条目数dict
,所以我非常喜欢只保留一组等价的一个myClass
中的对象dict
。 如此反复,似乎__eq__
需要计算时调用if myNewClassObj in dict
,这将玷污一个dict
的O(1)查找时间为O(n)查找时间
首先, __hash__(myNewMyClassObj)
被调用。 如果用相同的哈希没有对象在字典中发现,巨蟒假定myNewMyClassObj
是不是在字典。 (请注意,Python的要求,每当__eq__
评估两个对象为相等,它们的__hash__
必须相同。)
如果以相同的一些对象__hash__
在字典中发现, __eq__
被称为他们每个人。 如果__eq__
评估任何人作为平等的myNewMyClassObj in dict_
返回True。
因此,你只需要确保两个__eq__
和__hash__
快。
为了您的跟进问题:是的, dict_
只存储一组等价的一个MyClass
对象(如定义__eq__
)。 (如不设置)。
需要注意的是__eq__
只呼吁那些相同的散列和得到分配到同一个桶中的对象。 这样的对象的数量通常是一个非常小的数目( dict
实施,使得确保这一点)。 所以,你仍然有(大致) O(1)
查找性能。
__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__
没有在最后一种情况下调用,因为没有在字典中的对象具有相同的哈希值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
以上),有时必须检查所有的值。
__hash__定义对象被放入桶中,__eq__被称为只有当对象是在同一个桶。