在SO上常见的问题是从列表中删除重复项。由于列表是不可哈希的,set([[1, 2], [3, 4], [1, 2]])
会抛出TypeError: unhashable type: 'list'
。这类问题的答案通常涉及使用元组,因为元组是不可变的,因此是可哈希的。
什么使得列表不可哈希?的答案包括以下内容:
如果哈希值在存储在字典的特定槽位之后发生更改,则会导致不一致的字典。例如,最初列表将存储在位置A,该位置是基于哈希值确定的。如果哈希值发生更改,并且我们寻找列表,则可能无法在位置A找到它,或者根据新的哈希值,我们可能会找到其他对象。
但我不太明白,因为可以用作字典键的其他类型可以自由更改而没有问题:
>>> d = {}
>>> a = 1234
>>> d[a] = 'foo'
>>> a += 1
>>> d[a] = 'bar'
>>> d
{1234: 'foo', 1235: 'bar'}
很明显,如果
a
的值发生变化,它将哈希到字典中的不同位置。但是,对于列表来说,相同的假设为什么是危险的呢?为什么以下方法对于哈希列表是不安全的,尽管我们在需要时都会使用它?>>> class my_list(list):
... def __hash__(self):
... return tuple(self).__hash__()
...
>>> a = my_list([1, 2])
>>> b = my_list([3, 4])
>>> c = my_list([1, 2])
>>> foo = [a, b, c]
>>> foo
[[1, 2], [3, 4], [1, 2]]
>>> set(foo)
set([[1, 2], [3, 4]])
看起来这解决了set()
的问题,但这为什么是个问题呢?列表可能是可变的,但它们是有序的,这似乎就足够用于哈希。
a
。 - Martijn Pietersa += 1; print(d[a])
的结果是'foo'
,那么你的数字示例就是正确的。但事实并非如此。然而,使用一个已变异的列表就可以实现这一点(如果可变列表可以用作键,但它们不能,因为这样做没有意义)。 - deceze