为什么python允许元组作为字典的键

Qio*_*Liu 2 python dictionary tuples immutability

python 中的元组可以有不同类型的元素。例如:

tup1 = ('physics', 'chemistry', 1997, 2000);
tup2 = (1, 2, 3, 4 );
Run Code Online (Sandbox Code Playgroud)

当用于字典中的键时,当元素大小变化时,python如何决定键的大小?

Ant*_*ala 6

Python 字典不需要知道键的大小。Python 字典接受任何对象作为键,前提是它提供了__hash____eq__特殊方法。Python 通过 找到匹配的键key == another,它在内部调用key.__eq__(another). 这也意味着,您可以拥有一个同时包含字符串、整数、1 和 100 个元素的元组作为键的字典。

为了加快速度,字典将这些键组织成一个哈希表,该表使用计算的哈希码hash(key)来划分键;内部hash(key)调用 key.__hash__();哈希码是一个满足两个规则的简单整数:

  1. 一定是hash(key) == hash(another)如果key == another
  2. 应该选择散列键,以便如果key != another那么最好(但不是在所有情况下)hash(key) != hash(another)

此外,在 Pythonhash(x)中 的生命周期必须保持不变x,这意味着x与其他对象的相等性也不能改变。

元组同时具有__eq____hash__

>>> t = ('physics', 'chemistry', 1997, 2000)
>>> hash(t)
1710411284653490310
>>> u = ('physics', 'chemistry', 1997, 2000) # another tuple
>>> t is u  # they are not the same object
False
>>> hash(t) == hash(u)
True
>>> t == u
True
Run Code Online (Sandbox Code Playgroud)

现在,Python 甚至根本不需要使用哈希码在字典中查找对象,它只需要找到一个具有匹配键的元素,将每个元素与给定的键进行比较==。但这意味着在具有n键的字典上,平均n / 2必须进行比较才能找到键。使用散列技巧,我们可以缩小要比较的键集,理想情况下始终为 1 或最多为少数,因此无论大小,字典中的查找都应该同样快。


现在,与元组不同list,Python 中的 a 是可变值,因此不可能提供将来也满足上述 2 条规则的不变哈希码。因此 Python 根本没有定义它:

>>> [].__hash__ is None
True
Run Code Online (Sandbox Code Playgroud)

同样,如果将其用作字典键,则会出现异常:

>>> {[]: 42}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Run Code Online (Sandbox Code Playgroud)


NPE*_*NPE 5

当元素大小变化时,python如何决定key的大小?

简单的答案是它没有:Python 允许具有异构键的字典。这比不同大小的元组要广泛得多:

In [15]: d = {}

In [16]: d[42] = 'foo'

In [17]: d['bar'] = -1

In [18]: d[(1, 2, 3)] = {}

In [19]: d
Out[19]: {42: 'foo', 'bar': -1, (1, 2, 3): {}}
Run Code Online (Sandbox Code Playgroud)

任何可散列对象,无论其类型如何,都可以用作任何字典的键。