python基础问题请教一下,dict的key可以是([1, 2],)这种类型的吗?

众所周知,python的dict对象,是一个不可哈希对象,是可变的。他的key只接受 哈希类型 的数据,也就是 字符串、整型、浮点型、布尔、元组和None;而不可hash的有 字典、数组、集合是不能作为key的。

遇到一个疑问,没找到合理的解释,求释疑。

In [133]: a = {}

In [134]: a[(1, 2)] = 'abc'

In [135]: a
Out[135]: {(1, 2): 'abc'}

################
In [136]: a[([1, 2],)] = 'def'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [136], line 1
----> 1 a[([1, 2],)] = 'def'

TypeError: unhashable type: 'list'

这里面报错的部分是字典的key出现了不可哈希对象,list

但是我用 ([1, 2],) 包裹了,这个整体应当是一个哈希对象才对,而且是 tuple 不是 list。应当符合字典key的数据要求类型啊。

In [138]: from typing import Hashable

In [139]: isinstance(([1, 2],), Hashable)
Out[139]: True

通过校验结果,可以看到 ([1, 2],) 确实是可哈希的。但上面那个报错我就不太理解,希望有大佬能提点一下,没找到官方解释。


另外,补充一个例子:

In [141]: class example(object):
     ...:     def __init__(self, a):
     ...:         self.value = a
     ...:     def __eq__(self, rhs):
     ...:         return self.value == rhs.value
     ...:     def __hash__(self):
     ...:         return hash(self.value)
     ...:

In [150]: a = example(2)
     ...: d = {a: "first"}
     ...: a.data = 2
     ...: d[a] = 'second'

In [151]: d
Out[151]: {<__main__.example at 0x10a5d3d30>: 'second'}
In [153]: isinstance(a, Hashable)
Out[153]: True

官方文档,看到hashable的定义:

hashable

An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method). Hashable objects which compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.

Most of Python’s immutable built-in objects are hashable; mutable containers (such as lists or dictionaries) are not; immutable containers (such as tuples and frozensets) are only hashable if their elements are hashable.

Objects which are instances of user-defined classes are hashable by default. They all compare unequal (except with themselves), and their hash value is derived from their id().

阅读 1.7k
1 个回答

hashable: 对象的__hash__()函数。

看下tuple__hash__()函数实现。

https://github.com/python/cpy...
static Py_hash_t
tuplehash(PyTupleObject *v)
{
    Py_ssize_t i, len = Py_SIZE(v);
    PyObject **item = v->ob_item;

    Py_uhash_t acc = _PyHASH_XXPRIME_5;
    for (i = 0; i < len; i++) {
        Py_uhash_t lane = PyObject_Hash(item[i]);
        if (lane == (Py_uhash_t)-1) {
            return -1;
        }
        acc += lane * _PyHASH_XXPRIME_2;
        acc = _PyHASH_XXROTATE(acc);
        acc *= _PyHASH_XXPRIME_1;
    }

    /* Add input length, mangled to keep the historical value of hash(()). */
    acc += len ^ (_PyHASH_XXPRIME_5 ^ 3527539UL);

    if (acc == (Py_uhash_t)-1) {
        return 1546275796;
    }
    return acc;
}

会计算所有元素的哈希值。


其实就是tuple的hash值,是根据其里面所有元素的hash值确定的。如:

t1 = (1, 2, 3)
t1_hash = t1.__hash__()
# t1.__hash__() 这个值的计算 

# 类似这种计算
def tuple_hash(t1):
  t_hash = 0
  for i in t1:
      i_hash = i.__hash__()
      # 为了避免哈希碰撞,做了一系列处理
      t_hash += magic_func(i_hash)
  return t_hash
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏