根据我在Python中的理解,由于元组是不可变的,因此它们应该是可哈希的,而dotnet --version
函数应该可以在它们上工作。但是,似乎不是这样,因为当它们包含列表或字典之类的项目时,哈希函数会发出如下抱怨。
这有效:
hash()
这不起作用:
>>> t = (1, 2, 'name', 'Subhayan', 'age', 32, 'sex', 'male')
>>> hash(t)
3584505648807432737
所以我的问题是哈希函数如何在内部运行?它会遍历元组并尝试哈希各个组件吗?
有人可以给我一些参考吗?
答案 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
其中的“魔术数字”可确保元组哈希值为内容哈希值的微小变化生成宽范围的值。
那是最合乎逻辑的实现;散列应该反映所包含的值,并且当您针对另一个元组测试相等性时,所包含的值也将针对另一个元组中的对象进行测试;哈希和相等功能紧密相关。
组可以引用任何类型的对象;元组本身不能更改,但这并不意味着您不能更改它所引用的对象。这样,元组的哈希性取决于元组引用的对象的哈希性。