[英]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.