【问题标题】:Why can I not use a list as a dictionary key? [closed]为什么我不能使用列表作为字典键? [关闭]
【发布时间】:2014-05-09 06:22:39
【问题描述】:

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

hlst 是一个列表。 备忘录是一个字典。

if not hlst in memo:
    # do something
else:
    configurations = memo[hlst]

当我尝试它时,python 告诉我 hlist 是不可散列的。

【问题讨论】:

  • 不知道你在问什么。你的问题不清楚。
  • 如果hlst 是一个列表,它不可能在字典中。那你为什么要检查? (如果您想将列表添加到字典中,请先将其转换为元组,使用tuple(hlst):只需确保始终将任何其他值转换为元组,然后再将其用作键或检查其存在)
  • 不能附加字典,如果这就是你的意思。也许您想将字典附加到列表中?
  • 其实是个好问题。我已经对其进行了重新表述以使其更清晰。您不能将列表用作键,因为列表是可变的。由于它们是可变的,它们不能是可散列的(如字符串和元组)。假设hlist = ['a']。想想如果将 hlist 的内容更改为 ['b'] 会发生什么。 memo[['a']] 会返回什么?
  • 列表是可变的。字典要求键是不可变的,这样键,即键的哈希值,保持不变。将其转换为元组或字符串表示形式。或者,如果列表很大,请尝试其他一些实现。查看stackoverflow.com/questions/2671211/…

标签: python list dictionary hash


【解决方案1】:

您的问题是缺乏对“可散列”概念的理解。

我们称对象为“可散列的”,如果你能够计算它的哈希码。

Hashcode(又名哈希函数)是一个函数,它接受对象并返回一些通常应该足以将其与其他对象区分开来的值。这意味着,哈希码在某种程度上应该可以用作 ID。当然,会有所谓的“哈希冲突”(当两个对象具有相同的哈希码时),因为可能的对象比哈希码要多。

对哈希函数(用于获取对象哈希码的函数)的约束(比“不同的对象具有不同的哈希码”)更重要的是它在对象的整个生命周期中应该是相同的。

看:我们有对象a,它具有xy 的属性/属性。要使散列函数正常工作,您需要确定 a 的散列将不依赖于 xy

列表的哈希码取决于其值哈希码,因此它们本身是不可哈希的。为什么?因为如果您更改列表的一个元素(或添加一个或删除等),它的哈希码会更改(因为它依赖于该元素)。

现在,回到字典。字典是一个哈希表,可以描述为“简单的内存数组对,在索引(X模(数组大小))下有值,第一项有哈希码X”(这是一个相当简化,但主要概念贯穿语言和实现;pair的第一个值称为“key”,第二个“value”或“item”)。如果要将列表 [A, B] 与哈希码 1234 和值 V 插入大小为 10 的哈希表,然后将此列表的值更改为 [A, C] (这意味着将哈希值更改为 5678),然后在插入对 ([A, B], V) 的时刻将在索引 1234 模 10 = 4,但更改后它应该在索引 5678 模 10 = 8。

为了使其正常工作,我们需要在每次 key 对象更改时通知 hash table(这很丑陋,难以实现,并且占用大量资源),或者确保 key 的 hashcode 不会改变太久,因为它在哈希表中。 Python 创建者选择了第二个选项,因为它被广泛使用,证明运行良好且稳定。

这就是为什么 python 有两种有序集合类型 - 列表和元组的原因之一。你可能知道,元组是不可变的,所以它的哈希码不应该改变 - 因此,它可以用作字典键。

PS。上面的文字是相当简化的。列表哈希码对其元素的依赖性有点棘手。此外,在语言参考中没有定义字典作为 hashmap 的实现——它只说键应该是可散列的,没有解释原因。某些实现可以很好地处理不可散列的对象,但强制可散列键完全符合参考。

【讨论】:

    【解决方案2】:

    您不能使用列表作为键,因为列表是可变的。由于它们是可变的,它们不能是可散列的(如字符串和元组)。假设hlist = ['a']。想想如果将hlist 的内容更改为['b'] 会发生什么。 memo[['a']] 会返回什么?

    要克服这个问题,您可以根据 darkryder 的评论将您的列表变成一个元组 tuple(hlist)

    【讨论】:

    • 问题在于它实际上可能是一个包含列表列表的元组。如何转换所有级别?此外,常规类的实例是可散列的,您也可以对它们进行变异。
    猜你喜欢
    • 2019-03-30
    • 2011-11-07
    • 2023-04-06
    • 2022-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    相关资源
    最近更新 更多