当你调用`会发生什么,如果在dict`关键(What happens when you call `

2019-07-01 15:01发布

我有一个类(姑且称之为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)查找时间

Answer 1:

首先, __hash__(myNewMyClassObj)被调用。 如果用相同的哈希没有对象在字典中发现,巨蟒假定myNewMyClassObj是不是在字典。 (请注意,Python的要求,每当__eq__评估两个对象为相等,它们的__hash__必须相同。)

如果以相同的一些对象__hash__在字典中发现, __eq__被称为他们每个人。 如果__eq__评估任何人作为平等的myNewMyClassObj in dict_返回True。

因此,你只需要确保两个__eq____hash__快。

为了您的跟进问题:是的, dict_只存储一组等价的一个MyClass对象(如定义__eq__ )。 (如不设置)。

需要注意的是__eq__只呼吁那些相同的散列和得到分配到同一个桶中的对象。 这样的对象的数量通常是一个非常小的数目( dict实施,使得确保这一点)。 所以,你仍然有(大致) O(1)查找性能。



Answer 2:

__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以上),有时必须检查所有的值。



Answer 3:

__hash__定义对象被放入桶中,__eq__被称为只有当对象是在同一个桶。



文章来源: What happens when you call `if key in dict`