Python 为什么列表不能用作字典键?

Python 为什么列表不能用作字典键?,python,dictionary,Python,Dictionary,我想要一个字典中的键列表,定义如下: data = { [24,48,96]: ["QN.FN.EQ", "OT.AR.LN", "BL.VL.TR"] } 这不管用。。。错误是因为“列表类型不可哈希…” 有什么解决办法吗?为了能够像这样从字典中获取数据: data[[24,48,96]] # => ["QN.FN.EQ", "OT.AR.LN", "BL.VL.TR"] 我现在唯一的解决方案是——将列表转换为字符串并使用字符串作为键 data = { "24,48,96

我想要一个字典中的键列表,定义如下:

data = { 
  [24,48,96]: ["QN.FN.EQ", "OT.AR.LN", "BL.VL.TR"] 
}
这不管用。。。错误是因为“列表类型不可哈希…”

有什么解决办法吗?为了能够像这样从字典中获取数据:

data[[24,48,96]] # => ["QN.FN.EQ", "OT.AR.LN", "BL.VL.TR"]
我现在唯一的解决方案是——将列表转换为字符串并使用字符串作为键

data = { 
  "24,48,96": ["QN.FN.EQ", "OT.AR.LN", "BL.VL.TR"] 
}
arr = [24,48,96]
print(data[','.join(map(str,arr))])

您可以将元组用作字典键:

data = { 
    (24,48,96): ["QN.FN.EQ", "OT.AR.LN", "BL.VL.TR"] 
}

print data[(24,48,96)]

我正在回答这篇文章标题中的问题。:)

因为列表是可变的,dict键需要是可散列的,而散列可变对象是个坏主意,因为散列值应该基于实例属性计算

示例1:散列可变对象,其中散列值基于对象的可变特征

>>> class stupidlist(list):
...     def __hash__(self):
...         return len(self)
... 
>>> stupid = stupidlist([1, 2, 3])
>>> d = {stupid: 0}
>>> stupid.append(4)
>>> stupid
[1, 2, 3, 4]
>>> d
{[1, 2, 3, 4]: 0}
>>> stupid in d
False
>>> stupid in d.keys()
False
>>> stupid in list(d.keys())
True
在改变
愚蠢的
之后,由于散列发生了变化,因此在dict中再也找不到它。只有对dict键列表进行线性扫描才能找到
愚蠢的

示例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
集合中找到它们

示例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
中有两个以上的实例作为键或
集合中存在,就会发生哈希冲突

使用
d[key]
d中的key
查找正确的实例需要执行与dict的key中存在的
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)
正如您所看到的,我们的
StupidList\u集合
中的成员资格测试甚至比对整个
列表
进行线性扫描还要慢,而在没有哈希冲突加载的集合中,您可以获得预期的超快查找时间(因子500)



TL;DR:您可以使用
tuple(yourlist)
作为
dict
键,因为tuple是不可变的和可散列的。

为什么Python字典不能接受数组作为dict键


答:因为数组是python中的一个列表,它是可变的。在python中,可变的东西不能用于dict键。您只能将字符串或元组之类的东西用作键。

我认为在Python中不允许将可变数据类型用作键。这就是为什么元组有效,而列表无效。基本上,如果可以就地更改数据,就不能将其用作键。

键应该是不可变的,列表可以根据设计进行更改。用元组代替。用元组
(24,48,96)
代替列表。@Scott不,我需要
元组
!字典的工作原理是对键进行散列(计算一个数字),并使用该散列来决定在何处存储键和值。哈希几乎总是基于对象的内容。所以,如果你把一个列表作为一个键,它将根据它的内容存储在一个特定的位置。然后,如果列表发生了变异,哈希值就会改变。如果散列被更改,字典将查找错误的位置来检索存储的值。这就是为什么Python没有为列表提供
\uuuu hash\uuu()
方法的原因。我对你的问题做了一些编辑,
[24,48,96]
是一个列表,而不是数组。这很重要,因为我们还有
numpy
数组、
bytearray
array.array
。。。(我还为我的答案增加了一些时间安排。)很好,绝对是我所需要的!你最近为什么要删除它?我只是在阅读的过程中,很高兴你恢复了它。“BladeMight,我认为这可能是多余的,但后来我改变了主意。”