继this问题后,我很想知道什么时候是python对象的hash 计算?
__init__
时间,__hash__()
__hash__()
或这可能会根据对象的类型而有所不同吗?
为什么hash(-1) == -2
而其他整数等于它们的哈希?
答案 0 :(得分:22)
哈希值通常在每次使用时计算,因为您可以很容易地检查自己(见下文)。 当然,任何特定对象都可以自由地缓存其哈希值。例如,CPython字符串执行此操作,但元组不执行此操作(例如,请参阅this rejected bug report)。
CPython中的哈希值-1 signals an error。这是因为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
答案 1 :(得分:6)
答案 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