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

时间:2015-11-08 04:52:32

标签: python hash cpython

docs错误地声称

  

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

虽然我记得这一次是正确的,但是在当前版本的python(v2.7.10,v3.5.0)中,这样的对象散列等于它们的id显然不正确。

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

在文档的another part中,它表示哈希是来自id的派生。何时/为什么实施会发生变化,而哈希“返回”的数字是如何从“id”中获得的?

2 个答案:

答案 0 :(得分:4)

相关功能似乎是:

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;
}

(该代码来自here,然后用作tp_hash here中的type广告位。)这里的评论似乎没有给出理由直接使用指针(与id相同)。实际上,将该更改引入函数的提交是here,并声明更改的原因是:

  

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

指的是this问题,这解释了更改原因的更多原因。

答案 1 :(得分:2)

由于issue #5186,这在2009年发生了变化;通常的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.

current implementation获取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%的加速,具体取决于所进行的测试。

词汇表已经过时了;我见到你了already opened an issue with the project