【问题标题】:Why must dictionary keys be immutable?为什么字典键必须是不可变的?
【发布时间】:2014-08-04 17:46:48
【问题描述】:

为什么字典键必须是不可变的?我正在寻找一个简单、明确的原因,说明 Python 字典中的键有这种限制。

【问题讨论】:

标签: python dictionary key immutability


【解决方案1】:

在我的电脑上,有一个文件/etc/dictionaries-common/words 包含大量英文单词:

>>> with open("/etc/dictionaries-common/words") as f:
...     words = [line.strip() for line in f]
... 
>>> "python" in words
True
>>> "BDFL" in words
False

让我们创建一个字典来存储所有这些单词的长度:

>>> word_lengths = {w: len(w) for w in words}
>>> word_lengths["parrot"]
6

而且,只是为了好玩,我们将对原始单词列表进行洗牌:

>>> from random import shuffle
>>> shuffle(words)
>>> words[:5]
["Willie's", 'Araceli', 'accessed', 'engagingly', 'hobnobs']

嗯,hobnobs。无论如何......现在我们已经与words 搞混了一点,我们变得有点偏执(可能出于同样的原因,我们渴望 hobnobs),我们想要检查所有单词我们的word_lengths 字典在我们混合之后仍然在words 中:

>>> all(w in words for w in word_lengths)
True

嗯,我们到了那里,但在我的机器上花了三分钟多的时间——至少足够多吃几块美味的饼干了。仔细想想,原因很明显:我们有...

>>> len(words)
99171

... 将近十万个单词要检查,对于字典中的每一个单词,Python 都必须搜索我们混合的单词列表,直到找到匹配项。它不必总是检查整个列表,但平均而言,每次将是五万个单词(或列表的一半),总共 50,000 × 100,000 = 5,000,000,000 次测试。即使在这个神奇的技术时代,50 亿也是很多。

为了绝对确定(我通常不那么偏执;通常我只是困),让我们反过来检查一下,确保 words 中的所有内容仍在 word_lengths 中:

>>> all(w in word_lengths for w in words)
True

嘿,什么?这次是,就像,十分之一秒!是什么赋予了?你吓到我了,伙计……嘿,我的饼干呢?我刚才有,我很确定。

与可以按任何旧顺序排列的列表不同(因此确保其中有一些项目意味着依次检查每个项目直到找到它),字典更有效一些。派对上可能没那么有趣,但是,嘿,让它负责音乐,你知道吗?

字典无情高效的秘诀在于,对于每个项目,字典都会根据其内容计算键的哈希值(实际上只是一个整数),并使用该哈希值将项目存储在内存中的特定位置。然后,当您查找该项目时,它会再次计算密钥内容的哈希值,对自己说“好的,"python",哈希值是7036520087640895475 ...是的,我知道我必须把它放在哪里,然后”,然后直接到正确的内存位置找到它。所以这一次,它只需要进行十万次检查,而不是五十亿次。

这有点像将所有 CD 整齐地按字母顺序放在架子上,而不是从它们的盒子里随意堆放在扬声器顶部。字典知道它在哪里,我告诉你。

但要让字典保持一致,这是有代价的。还记得我说过字典会根据项目的内容计算散列吗?那么,如果内容发生变化会发生什么?对于不成问题的不可变对象——它们的内容不能改变——但是可变对象,根据定义,可以改变它们的内容,当它们改变时,它们的哈希值(如果他们甚至有一个)也会改变。这很酷,显然,不是每个人都想被放在一个盒子里,我明白了,但是如果哈希值发生了变化,字典就无法确定它把东西放在哪里了。

就好像 Joy Division 把他们的名字改成了 New Order,而现在你不知道你把 Blue Monday 的 12 英寸混音放在哪里了。这行不通。

所以,字典有一个规则:如果你想成为一把钥匙,不要去改变

【讨论】:

  • 你设法在溢出博客中被提及,你应该得到它:) stackoverflow.blog/2020/11/13/…
  • 本来想为欢乐部门/新秩序的类比投票,但看了你的个人资料后,我现在要投票了,因为你是我在这里看到的第一个本地人。
【解决方案2】:

作为 Fredrik Lundh states here:

字典的哈希表实现使用哈希值 从键值计算来找到键。如果钥匙是 可变对象,它的值可以改变,因此它的哈希也可以 改变。但是因为无论谁改变了关键对象都不能告诉它 被用作字典键,它不能移动条目 词典。然后,当您尝试在 字典找不到,因为它的哈希值不同。如果 你试图查找它也找不到的旧值, 因为在该哈希箱中找到的对象的值将是 不同。

【讨论】:

    猜你喜欢
    • 2013-03-04
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 2019-06-20
    • 2019-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多