簡體   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