繁体   English   中英

当你调用`if key in dict`时会发生什么

[英]What happens when you call `if key in dict`

我有一个实现__hash____eq__的类(让我们称之为myClass )。 我还有一个将myClass对象映射到某个值的dict ,计算需要一些时间。

在我的程序过程中,许多(大约数百万) myClass对象被实例化。 这就是我使用dict跟踪这些值的原因。

但是,有时新的myClass对象可能等同于旧的myClass对象(由__eq__方法定义)。 因此,我不是再次计算该对象的值,而是在dict查找较旧的myClass对象的值。 为了实现这一点,我if myNewMyClassObj in dict执行if myNewMyClassObj in dict

这是我的问题:

当我in子句中使用它in ,会调用什么, __hash____eq__ 使用dict是它是O(1)查找时间。 那么必须调用__hash__ 但是如果__hash____eq__不是等价的方法怎么办? 在那种情况下, if myNewMyClassObj in dict我会得到误报吗?

跟进问题:

我要尽量减少我的条目数dict ,所以我非常喜欢只保留一组等价的一个myClass中的对象dict 再说一遍,似乎在计算if myNewClassObj in dict时需要调用__eq__ ,这会将dict的O(1)查找时间玷污为O(n)查找时间

首先, __hash__(myNewMyClassObj) 如果在字典中找不到具有相同哈希的对象,则Python假定myNewMyClassObj不在字典中。 (请注意,Python要求每当__eq__对两个对象求值相等时,它们的__hash__必须相同。)

如果在字典中找到具有相同__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__ 当对象在dict中时,每次查找都会调用__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需要显式检查dict中所有值的相等性,直到找到相等的值(或者发现它们都不相等)它正在寻找的那个)。 有时它会在第一次尝试时找到它(上面的Foo(1) in dict ),有时它必须检查所有值。

__hash__定义了放置对象的存储区,只有当对象位于同一存储桶中时才会调用__eq__。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM