为什么可变实体不能成为字典键?

时间:2016-05-12 21:38:36

标签: python dictionary

考虑以下Python解释器shell会话:

>>> class D(dict):
...   def __hash__(self):
...     return id(self)
... 
>>> d1 = D({'a': 'b'})
>>> d2 = D({'a1': 'b1'})
>>> t = {d1: 1, d2: 2}
>>> t[d1]
1
>>> t[d2]
2

为什么没有__hash__默认为id()?导致禁止将可变实体用作字典键的设计决定是什么?

3 个答案:

答案 0 :(得分:6)

  

为什么dict的$description = trim($_POST['description']); $check = $db_con->prepare("SELECT * FROM tb_keywords"); $check->execute(); while($row = $check->fetch(PDO::FETCH_ASSOC)) { $search = $row['keyword']; if (preg_match("/\b$search\b/", $description)) { $create=$db_con->prepare("INSERT INTO tb_post(description) VALUES(:description)"); $create->bindParam(":description", $description); $create->execute(); $row=$create->rowCount(); if($row>0) { echo "success"; } else { echo "fail"; } } } 默认为__hash__

因为这违反了基本不变量,即相等的物体具有相等的哈希值。如果dicts使用他们的id()作为他们的哈希值,那么你将有如下的交互:

id

这种行为会造成混淆,不一致和无用。

答案 1 :(得分:3)

dict的{​​{1}}不会默认为__hash__,因为它会破坏哈希表的基本用途。

this comment on another question's answer中链接:

  

请参阅Python常见问题条目Why must dictionary keys be immutable?

链接文档说明了以下内容(对于想要阅读更多内容的人的其他说明):

  

字典的哈希表实现使用从键值计算的哈希值来查找键。如果密钥是可变对象,则其值可能会发生变化,因此其哈希值也会发生变化。但是,由于无论谁更改密钥对象都无法判断它是否被用作字典密钥,因此无法在字典中移动条目。然后,当您尝试在字典中查找相同的对象时,将无法找到它,因为其哈希值不同。如果您尝试查找旧值,也不会找到它,因为在该哈希箱中找到的对象的值会有所不同。

强调补充。

基本上,哈希表仅在它只包含不可变值时才有用。像id()这样的解决方法会破坏整个目的,因为(如上面引用的粗体部分所述),您找不到您尝试查找的值。

答案 2 :(得分:1)

一般来说,基于价值而非身份的平等更有用和直观。例如,请考虑以下代码段:

from collections import Counter
words = 'dog cat dog'.split()
print Counter(words)  # Counter({'dog': 2, 'cat': 1})

这是有道理的。这是预料之中的。现在,如果事情顺利进行,我们就会有:

dicts = [{}, {}]
print Counter(dicts)  # Counter({{}: 1, {}: 1})

这不是我期望的第一件事。你可以向我解释一下,但是第一次遇到它(特别是如果我是编程的新手),它可能会让我在调试时感到非常沮丧。即使在了解它之后,偶尔会让我措手不及。因此,虽然语言可以以您的方式设计,但它不会对用户友好。

类似地,字典可以基于内容进行散列,这将使上述代码段更直观。但现在,如果我改变钥匙,我会遇到严重的麻烦。我可能会被警告不要,而是让语言保护我不要犯这种错误,而不是给予我自己想要在脚下射击的所有自由。

更好的方法是使用不可变的dict,您可以获得here