继this问题之后,我有兴趣知道Python对象的哈希值何时计算?
__init__
时间,__hash__()
被叫到,__hash__()
时,或这可能会根据对象的类型而有所不同吗?
为什么
hash(-1) == -2
而其他整数却等于它们的哈希值?
哈希值通常在每次使用时计算,因为您可以很容易地自行检查(见下文)。 当然,任何特定对象都可以自由缓存其哈希值。例如,CPython 字符串会执行此操作,但元组不会执行此操作(例如,请参阅此被拒绝的错误报告了解原因)。
哈希值 -1 在 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__
方法。元组通常被认为是不可变的,因此您可能认为可以缓存哈希值,但实际上元组的哈希值取决于其内容的哈希值,并且可能包含可变值。
对于不相信
str
的子类可以修改哈希值的@max:
>>> 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