用户定义类的默认哈希是什么?

4
文档错误地声称:

默认情况下,用户自定义类的实例对象是可哈希的;它们都不相等(除了自己),它们的哈希值是它们的id()

尽管我记得这曾经是正确的,但在当前版本的Python(v2.7.10、v3.5.0)中,这些对象的哈希值等于它们的id已经不再成立。

>>> class A:
...     pass
... 
>>> a = A()
>>> hash(a)
-9223372036578022804
>>> id(a)
4428048072

在文档的另一部分中提到,哈希值是从id派生而来的。实现何时/为何更改,并且现在哈希返回的数字是如何“从”id派生的?

2个回答

6
相关函数似乎是:
Py_hash_t
_Py_HashPointer(void *p)
{
    Py_hash_t x;
    size_t y = (size_t)p;
    /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid
       excessive hash collisions for dicts and sets */
    y = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4));
    x = (Py_hash_t)y;
    if (x == -1)
        x = -2;
    return x;
}

(该代码来自这里,然后被用作这里type中的tp_hash插槽。)那里的注释似乎给出了不直接使用指针(即与id相同的内容)的原因。实际上,引入该更改的提交在这里,并且说明了更改的原因是:

问题#5186:通过将对象指针向右旋转4位来减少没有 hash 方法的对象的哈希冲突。

其中提到了问题,该问题更详细地解释了为什么要进行此更改。


2
这是在2009年因 #5186问题而改变的,通常的id()值会导致太多的冲突:
In the issue 5169 discussion, Antoine Pitrou suggested that for an object 
x without a `__hash__` method, `id()/8` might be a better hash value than 
`id()`, since dicts use the low order bits of the hash as initial key, and 
the 3 lowest bits of an `id()` will always be zero.

当前实现获取ID并旋转它以生成更多样化的值:
long
_Py_HashPointer(void *p)
{
    long x;
    size_t y = (size_t)p;
    /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid
       excessive hash collisions for dicts and sets */
    y = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4));
    x = (long)y;
    if (x == -1)
        x = -2;
    return x;
}

这导致速度提升了14%到34%,具体取决于所执行的测试。
术语表已经过时了;我看到你已经向该项目提交了一个问题

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接