繼續從this問題,我很想知道什麼時候是python對象的散列計算?何時計算了python對象的散列值,並且爲什麼是-1的散列值不同?
- 在實例的
__init__
時間, - 首次
__hash__()
被調用時, - 每次
__hash__()
被調用,或 - 我可能會丟失任何其他的機會呢?
這可能會根據對象的類型而變化嗎?
爲什麼hash(-1) == -2
而其他整數等於它們的散列?
繼續從this問題,我很想知道什麼時候是python對象的散列計算?何時計算了python對象的散列值,並且爲什麼是-1的散列值不同?
__init__
時間,__hash__()
被調用時,__hash__()
被調用,或這可能會根據對象的類型而變化嗎?
爲什麼hash(-1) == -2
而其他整數等於它們的散列?
通常在每次使用散列時計算散列,因爲您可以很容易地檢查自己(請參閱下文)。 當然,任何特定的對象都可以自由緩存它的散列。例如,CPython字符串執行此操作,但元組不會(例如,請參閱this rejected bug report)。
散列值-1
signalizes 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
這是很容易看到該選項#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
如果您將包含可變值的元組作爲參數傳遞給內置函數,在散列函數中,將引發TypeError異常。所以這不是元組不會緩存哈希值的原因。 [@ PetrViktorin上面的答案](http://stackoverflow.com/a/7648538/336527)開頭的鏈接提供瞭解釋。另見[Guido的評論](http://mail.python.org/pipermail/python-dev/2003-August/037424.html)。另外,你確定散列不被str子類緩存嗎?它似乎返回與str.hash相同的值,它會自動緩存。 – max
@max,我爲你添加了一個例子,表明一個'str'子類的散列沒有被緩存。 – Duncan
啊是的,對..我想我在想,如果你沒有定義'__hash__',它會被緩存,但這很明顯,因爲它只是使用'str .__ hash__'。 – max
3超出正如我[閱讀](http://www.laurentluce.com/posts/ python-dictionary-implementation /)它在第一次被調用時被緩存。我會假設第二個選項是正確的,但因爲我不知道我不會將它作爲答案:) – rplnt
@rplnt:wrong;這只是在談論字典時。它的哈希將被存儲在字典中,但這不是一般性哈希。 –
@ChrisMorgan其實我不認爲python'dict'爲它的密鑰緩存哈希值。當然,單個類可以在'__hash__'函數中做任何他們喜歡的事情,所以上面引用的文章表示'str'緩存了它的散列值。 – max