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

时间:2018-10-25 20:59:53

标签: 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": ["QN.FN.EQ", "OT.AR.LN", "BL.VL.TR"] 
}
arr = [24,48,96]
print(data[','.join(map(str,arr))])

4 个答案:

答案 0 :(得分:5)

您可以使用元组作为字典键:

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

print data[(24,48,96)]

答案 1 :(得分:3)

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

因为列表是可变的,所以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

更改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

这也不是一个好主意,因为相等的对象应该相同地散列,以便可以在dictset中找到它们。

示例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键,因为元组是不可变且可哈希的。

答案 2 :(得分:1)

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

答案:因为数组是python中的一个列表,该列表是可变的。可变的东西不能在python中使用dict键。您只能使用不可变之类的东西,例如string或tuple作为键。

答案 3 :(得分:0)

我认为您不允许在Python中使用可变数据类型作为键。这就是为什么元组起作用而列表不起作用的原因。本质上,如果您可以就地更改数据,则不能将其用作键。