Python 3.2中__hash__是如何实现的?

5 投票
2 回答
2237 浏览
提问于 2025-04-16 17:39

我想让自定义对象可以被哈希(通过序列化)。我找到了Python 2.x的__hash__算法(见下面的代码),但显然它和Python 3.2的哈希不一样(我想知道这是为什么?)。有没有人知道Python 3.2中__hash__是怎么实现的?

#Version: Python 3.2

def c_mul(a, b):
    #C type multiplication
    return eval(hex((int(a) * b) & 0xFFFFFFFF)[:-1])

class hs:
    #Python 2.x algorithm for hash from http://effbot.org/zone/python-hash.htm
    def __hash__(self):
        if not self:
            return 0 # empty
        value = ord(self[0]) << 7
        for char in self:
            value = c_mul(1000003, value) ^ ord(char)
        value = value ^ len(self)
        if value == -1:
            value = -2
        return value


def main():
    s = ["PROBLEM", "PROBLEN", "PROBLEO", "PROBLEP"]#, "PROBLEQ", "PROBLER", "PROBLES"]
    print("Python 3.2 hash() bild-in")
    for c in s[:]: print("hash('", c, "')=", hex(hash(c)),  end="\n")
    print("\n")
    print("Python 2.x type hash: __hash__()")
    for c in s[:]: print("hs.__hash__('", c, "')=", hex(hs.__hash__(c)),  end="\n")


if __name__ == "__main__":
    main()

OUTPUT:
Python 3.2 hash() bild-in
hash(' PROBLEM ')= 0x7a8e675a
hash(' PROBLEN ')= 0x7a8e6759
hash(' PROBLEO ')= 0x7a8e6758
hash(' PROBLEP ')= 0x7a8e6747


Python 2.x type hash: __hash__()
hs.__hash__(' PROBLEM ')= 0xa638a41
hs.__hash__(' PROBLEN ')= 0xa638a42
hs.__hash__(' PROBLEO ')= 0xa638a43
hs.__hash__(' PROBLEP ')= 0xa638a5c

2 个回答

3

我查了一下源代码中的新功能(在unicodeobject.c文件里),然后在Python中重建了它。下面是代码:

def my_hash(string):
    x = ord(string[0]) << 7
    for c in string:
        x = (1000003 * x) ^ ord(c)
    x ^= len(string)
    needCorrection =  x & (1 << 65)
    x %= 2 ** 64
    if needCorrection:
        x = -~(-x ^ 0xFFFFFFFFFFFFFFFF)
    if x == -1:
        x = -2
    return x

不过,这个功能只适用于64位系统。而且现在修正了Python在数字变成负数时的奇怪表现。(你最好不要想太多这个问题。)

5

它们为什么会不同的原因在这里写着:

哈希值现在是一个新的类型,叫做 Py_hash_t,这个类型的大小和指针是一样的。之前哈希值是长整型(long),在一些64位的操作系统上,它的长度仍然只有32位。

哈希计算时还考虑了新的值,看看

 sys.hash_info 

对于字符串,你可以查看 http://svn.python.org/view/python/trunk/Objects/stringobject.c?view=markup 第1263行 string_hash(PyStringObject *a)

撰写回答