元组的哈希函数如何工作

时间:2018-08-05 13:37:05

标签: python hash

根据我在Python中的理解,由于元组是不可变的,因此它们应该是可哈希的,而dotnet --version 函数应该可以在它们上工作。但是,似乎不是这样,因为当它们包含列表或字典之类的项目时,哈希函数会发出如下抱怨。

这有效:

hash()

这不起作用:

>>> t = (1, 2, 'name', 'Subhayan', 'age', 32, 'sex', 'male')
>>> hash(t)
3584505648807432737

所以我的问题是哈希函数如何在内部运行?它会遍历元组并尝试哈希各个组件吗?

有人可以给我一些参考吗?

1 个答案:

答案 0 :(得分:4)

是的,不可变容器的哈希采用所包含对象的哈希值。

有关元组的信息,请参见Objects/tupleobject.c源代码,尤其是tuplehash function

static Py_hash_t
tuplehash(PyTupleObject *v)
{
    Py_uhash_t x;  /* Unsigned for defined overflow behavior. */
    Py_hash_t y;
    Py_ssize_t len = Py_SIZE(v);
    PyObject **p;
    Py_uhash_t mult = _PyHASH_MULTIPLIER;
    x = 0x345678UL;
    p = v->ob_item;
    while (--len >= 0) {
        y = PyObject_Hash(*p++);
        if (y == -1)
            return -1;
        x = (x ^ y) * mult;
        /* the cast might truncate len; that doesn't change hash stability */
        mult += (Py_hash_t)(82520UL + len + len);
    }
    x += 97531UL;
    if (x == (Py_uhash_t)-1)
        x = -2;
    return x;
}

在Python中,忽略C int类型溢出,等效项为:

_PyHASH_MULTIPLIER = 1000003  # from pyhash.h

def tuplehash(v):
    x = 0x345678
    mult = _PyHASH_MULTIPLIER
    l = len(v)
    for ob in v:
        y = hash(ob)
        x = ((x ^ y) * mult)
        mult += (82520 + l + l)
    x += 97531
    return x

其中的“魔术数字”可确保元组哈希值为内容哈希值的微小变化生成宽范围的值。

那是最合乎逻辑的实现;散列应该反映所包含的值,并且当您针对另一个元组测试相等性时,所包含的值也将针对另一个元组中的对象进行测试;哈希和相等功能紧密相关。

组可以引用任何类型的对象;元组本身不能更改,但这并不意味着您不能更改它所引用的对象。这样,元组的哈希性取决于元组引用的对象的哈希性。