0

一个可散列的对象需要一个__hash__方法,并且它有一个在其生命周期内永远不会改变的散列值。

由于我完全忽略的原因,Python 列表不可散列,我想知道以下实现是否正常,或者它是否有一些我不知道的故障。

class L(list):
    def __hash__(self):
        return id(self)

 a = range(10)
 l = L(range(10))
 print a
 >> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 print l
 >> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 hash(l)
 >> 41889288
 hash(a) # unsurprisingly it returns an error because a list is not hashable
 >> Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: unhashable type: 'list'

 # lets append a value to l
 l.append(1000)
 print l
 >> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1000]
 isinstance(l, list) # to double check whether l is a List instance
 >> True
 D = {}
 D[l] = "this is a dict with a list as key"
 print D
 {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1000]: 'this is a dict with a list as key'}
 # lets append another value to l
 l.append(-10)
 print D
 >> {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1000, -10]: 'this is a dict with a list as key'}

 # now lets add a normal list to the dict to the dict
 D[a] = "Add a list as key"
 >>Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   TypeError: unhashable type: 'list'

所以这是一个可散列的列表实现,它几乎可以毫无问题地工作,我不明白为什么一个列表在正常的 Python 发行版中不能是可散列的,即使它仍然是可变的。

注意:这个问题不是关于为什么不能将列表用作字典键的任何解释?

4

2 回答 2

7

你有一个__hash__方法。它返回一些东西。它会返回有用的东西吗?让我们来看看。

>>> a = L([1, 2])
>>> b = L([1, 2])
>>> a == b
True
>>> s = set([a, b])
>>> s
set([[1, 2], [1, 2]])

没有!它打破了任何使用 的代码所做的假设hash,特别是相等的对象具有相等的哈希值。现在我们有一个包含两个相等对象的集合。您可以通过按身份进行制作==!=比较来解决此问题,但是您也会丢失总顺序并且您必须删除<, >,<=>=. 这是很多有用的东西,你必须拿出来使列表有意义地可散列。

于 2015-05-29T19:39:32.660 回答
0

您可以(但有限制)使用 JSON 转储来比较内容质量。一个限制是这仅适用于任何 JSON 可序列化数据。

import json

class HashableList(list):
    def __hash__(self):
        return hash(json.dumps(self, sort_keys=True))

这在大多数情况下工作得很好。无论如何,您需要确保这适用于您使用它的数据。

于 2020-01-19T09:54:12.403 回答