2011-10-04 44 views
22

繼續從this問題,我很想知道什麼時候是python對象的散列計算何時計算了python對象的散列值,並且爲什麼是-1的散列值不同?

  1. 在實例的__init__時間,
  2. 首次__hash__()被調用時,
  3. 每次__hash__()被調用,或
  4. 我可能會丟失任何其他的機會呢?

這可能會根據對象的類型而變化嗎?

爲什麼hash(-1) == -2而其他整數等於它們的散列?

+0

3超出正如我[閱讀](http://www.laurentluce.com/posts/ python-dictionary-implementation /)它在第一次被調用時被緩存。我會假設第二個選項是正確的,但因爲我不知道我不會將它作爲答案:) – rplnt

+0

@rplnt:wrong;這只是在談論字典時。它的哈希將被存儲在字典中,但這不是一般性哈希。 –

+0

@ChrisMorgan其實我不認爲python'dict'爲它的密鑰緩存哈希值。當然,單個類可以在'__hash__'函數中做任何他們喜歡的事情,所以上面引用的文章表示'str'緩存了它的散列值。 – max

回答

19

通常在每次使用散列時計算散列,因爲您可以很容易地檢查自己(請參閱下文)。 當然,任何特定的對象都可以自由緩存它的散列。例如,CPython字符串執行此操作,但元組不會(例如,請參閱this rejected bug report)。

散列值-1signalizes an error給CPython。這是因爲C沒有例外,所以它需要使用返回值。當一個Python對象的__hash__返回-1時,CPython實際上會默默地將其更改爲-2。

見自己:

class HashTest(object): 
    def __hash__(self): 
     print('Yes! __hash__ was called!') 
     return -1 

hash_test = HashTest() 

# All of these will print out 'Yes! __hash__ was called!': 

print('__hash__ call #1') 
hash_test.__hash__() 

print('__hash__ call #2') 
hash_test.__hash__() 

print('hash call #1') 
hash(hash_test) 

print('hash call #2') 
hash(hash_test) 

print('Dict creation') 
dct = {hash_test: 0} 

print('Dict get') 
dct[hash_test] 

print('Dict set') 
dct[hash_test] = 0 

print('__hash__ return value:') 
print(hash_test.__hash__()) # prints -1 
print('Actual hash value:') 
print(hash(hash_test)) # prints -2 
5

here

散列值-1是保留(它被用來標記錯誤在C實現)。 如果散列算法生成此值,我們只需使用-2。

由於整數的散列是整數本身,它只是馬上改變。

1

這是很容易看到該選項#3適用於用戶定義的對象。如果你改變對象的話,這可以改變散列值,但是如果你將對象用作字典鍵,你必須確保防止散列值的變化。

>>> class C: 
    def __hash__(self): 
     print("__hash__ called") 
     return id(self) 


>>> inst = C() 
>>> hash(inst) 
__hash__ called 
43795408 
>>> hash(inst) 
__hash__ called 
43795408 
>>> d = { inst: 42 } 
__hash__ called 
>>> d[inst] 
__hash__ called 

字符串使用選項#2:他們計算一次散列值並緩存結果。這是安全的,因爲字符串是不可變的,所以散列永遠不會改變,但是如果子類str的結果可能不是不可變的,所以每次都會調用__hash__方法。元組通常被認爲是不可變的,因此您可能認爲哈希可能被緩存,但實際上,元組的哈希取決於其內容的哈希值,並且可能包含可變值。

對於@Max誰不相信的str子類可以改變哈希:

>>> class C(str): 
    def __init__(self, s): 
     self._n = 1 
    def __hash__(self): 
     return str.__hash__(self) + self._n 


>>> x = C('hello') 
>>> hash(x) 
-717693723 
>>> x._n = 2 
>>> hash(x) 
-717693722 
+0

如果您將包含可變值的元組作爲參數傳遞給內置函數,在散列函數中,將引發TypeError異常。所以這不是元組不會緩存哈希值的原因。 [@ PetrViktorin上面的答案](http://stackoverflow.com/a/7648538/336527)開頭的鏈接提供瞭解釋。另見[Guido的評論](http://mail.python.org/pipermail/python-dev/2003-August/037424.html)。另外,你確定散列不被str子類緩存嗎?它似乎返回與str.hash相同的值,它會自動緩存。 – max

+0

@max,我爲你添加了一個例子,表明一個'str'子類的散列沒有被緩存。 – Duncan

+0

啊是的,對..我想我在想,如果你沒有定義'__hash__',它會被緩存,但這很明顯,因爲它只是使用'str .__ hash__'。 – max

相關問題