你有一个误解,hash算法针对的是元素的内容,并不是针对指针,所以指针不变不等于可hash。
如果你想深究细节的话,可以看tuple的源码:
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;
}
注意这里有一个循环,tuple会对其中每个元素调用PyObject_Hash,将结果进行异或运算再加上一个常量作为整个tuple的hash。
如果tuple中有一个元素是list,那么PyObject_Hash将调用list的hash算法,然而list定义就表明它根本不实现hash:
PyTypeObject PyList_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"list",
sizeof(PyListObject),
0,
(destructor)list_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)list_repr, /* tp_repr */
0, /* tp_as_number */
&list_as_sequence, /* tp_as_sequence */
&list_as_mapping, /* tp_as_mapping */
PyObject_HashNotImplemented, /* tp_hash */
...
再看 PyObject_HashNotImplemented 的实现:
Py_hash_t
PyObject_HashNotImplemented(PyObject *v)
{
PyErr_Format(PyExc_TypeError, "unhashable type: '%.200s'",
Py_TYPE(v)->tp_name);
return -1;
}
这个信息很眼熟吧?
结论就是,只要tuple中有一个元素是不可hash的(不限于list),那么整个tuple都是不可hash的。