什么时候计算python对象的哈希值,为什么-1的哈希值不同?

时间:2011-10-04 12:48:39

标签: python hash

this问题后,我很想知道什么时候是python对象的hash 计算

  1. 在实例的__init__时间,
  2. 第一次调用__hash__()
  3. 每次调用__hash__()
  4. 我可能遗失的任何其他机会?
  5. 这可能会根据对象的类型而有所不同吗?

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

3 个答案:

答案 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)

来自here

  

保留哈希值-1(它用于标记C实现中的错误)。   如果哈希算法生成此值,我们只需使用-2代替。

由于整数的散列本身就是整数,所以它立即就会改变。

答案 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