我将回答这篇文章标题中的问题。因为列表是可变的,字典键需要是可哈希的,而哈希可变对象是一个坏主意,因为哈希值应该基于实例属性计算。下面是示例1:哈希一个可变对象,其中哈希值基于对象的可变特征。
>>> class stupidlist(list):
... def __hash__(self):
... return len(self)
...
>>> stupid = stupidlist([1, 2, 3])
>>> d =
>>> stupid.append(4)
>>> stupid
[1, 2, 3, 4]
>>> d
>>> stupid in d
False
>>> stupid in d.keys()
False
>>> stupid in list(d.keys())
True
在改变了stupid
之后,它的哈希值发生了变化,因此无法在字典中找到。只有对字典键列表进行线性扫描才能找到stupid
。
示例2:...但为什么不使用固定的哈希值呢?
>>> class stupidlist2(list):
... def __hash__(self):
... return id(self)
...
>>> stupidA = stupidlist2([1, 2, 3])
>>> stupidB = stupidlist2([1, 2, 3])
>>>
>>> stupidA == stupidB
True
>>> stupidA in {stupidB: 0}
False
这也不是一个好主意,因为相等的对象应该具有相同的哈希值,以便您可以在 dict
或 set
中找到它们。
示例3: ... 好吧,那么所有实例的哈希值都是常数怎么样?!
>>> class stupidlist3(list):
... def __hash__(self):
... return 1
...
>>> stupidC = stupidlist3([1, 2, 3])
>>> stupidD = stupidlist3([1, 2, 3])
>>> stupidE = stupidlist3([1, 2, 3, 4])
>>>
>>> stupidC in {stupidD: 0}
True
>>> stupidC in {stupidE: 0}
False
>>> d = {stupidC: 0}
>>> stupidC.append(5)
>>> stupidC in d
True
虽然事情似乎符合预期,但请考虑正在发生的事情:当你的类的所有实例产生相同的哈希值时,在 dict
中作为键或在 set
中出现时,就会发生哈希冲突。
使用 d[key]
或 key in d
找到正确的实例需要执行与 stupidlist3
的实例数量相同的等式检查。此时,字典的目的 - O(1)查找 - 完全被破坏了。这在以下时间测量中得到了证明(使用 IPython 进行)。
一些时间测量
>>> lists_list = [[i] for i in range(1000)]
>>> stupidlists_set = {stupidlist3([i]) for i in range(1000)}
>>> tuples_set = {(i,) for i in range(1000)}
>>> l = [999]
>>> s = stupidlist3([999])
>>> t = (999,)
>>>
>>> %timeit l in lists_list
25.5 µs ± 442 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit s in stupidlists_set
38.5 µs ± 61.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit t in tuples_set
77.6 ns ± 1.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
正如您所看到的,在我们的
stupidlists_set
中进行成员测试甚至比整个
lists_list
的线性扫描还要慢,而在没有大量哈希冲突的集合中,您可以得到预期的超快查找时间(因子500)。
TL; DR: 您可以使用
tuple(yourlist)
作为
dict
键,因为元组是不可变且可哈希的。
(24, 48, 96)
,而非列表。 - cole元组(tuple)
! - BladeMight__hash __()
方法的原因。 - Steven Rumbalski[24,48,96]
是一个列表,而不是数组。这很重要,因为我们还有numpy
数组、bytearray
和array.array
...(我还在我的答案中添加了一些时间)。 - timgeb