Python 对象的哈希值何时计算?为什么 -1 的哈希值不同?

问题描述 投票:0回答:3

this问题之后,我有兴趣知道Python对象的哈希值何时计算

  1. 在某个实例的
    __init__
    时间,
  2. 第一次
    __hash__()
    被叫到,
  3. 每次调用
    __hash__()
    时,或
  4. 我可能会错过任何其他机会吗?

这可能会根据对象的类型而有所不同吗?

为什么

hash(-1) == -2
而其他整数却等于它们的哈希值?

python hash
3个回答
28
投票

哈希值通常在每次使用时计算,因为您可以很容易地自行检查(见下文)。 当然,任何特定对象都可以自由缓存其哈希值。例如,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

8
投票

这里

哈希值-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__
方法。元组通常被认为是不可变的,因此您可能认为可以缓存哈希值,但实际上元组的哈希值取决于其内容的哈希值,并且可能包含可变值。

对于不相信

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
© www.soinside.com 2019 - 2024. All rights reserved.