这是什么意思?
唯一不能作为字典键接受的值是包含列表或字典或其他可变类型的值,这些值通过值而不是按对象标识进行比较,原因是字典的有效实现需要保留键的哈希值恒定。
我认为即使是元组,也会按价值进行比较。
答案 0 :(得分:6)
将可变对象作为关键字的问题是,当我们使用字典时,我们很少想要检查身份。例如,当我们使用这样的字典时:
a = "bob"
test = {a: 30}
print(test["bob"])
我们希望它能够正常工作 - 第二个字符串"bob"
可能与a
不同,但它是相同的值,这是我们关心的。这适用于任何两个等同的字符串将具有相同的散列,这意味着dict
(实现为散列映射)可以非常有效地找到这些字符串。
当我们将列表作为关键字时,问题就会发挥作用,想象一下这个案例:
a = ["bob"]
test = {a: 30}
print(test[["bob"]])
我们不能再这样做了 - 比较不会起作用,因为列表的哈希不是基于它的值,而是基于列表的实例(又名(id(a) != id(["bob"))
)。
Python可以选择使列表的哈希值发生变化(破坏哈希映射的效率),或者只是简单地比较身份(在大多数情况下这是无用的)。 Python不允许这些特定的可变密钥来避免细微但常见的错误,人们希望这些错误等于值,而不是身份。
答案 1 :(得分:1)
文档将两个不同的东西混合在一起:可变性和价值可比性。我们将它们分开。
按身份比较的不可变对象很好。身份可以 永远不要改变任何对象。
按值比较的不可变对象很好。价值永远不会 更改不可变对象。这包括元组。
按身份比较的可变对象很好。身份可以 永远不要改变任何对象。
不接受按值比较的可变对象。价值 可以改变一个可变对象,这将使字典 无效。
与此同时,您的措辞与地图类型(4.10 in Python 3.3或5.8 in Python 2.7并不完全相同,两者都说:
字典的键几乎是任意值。不可清除的值,即包含列表,字典或其他可变类型的值(通过值而不是按对象标识进行比较)不能用作键。
无论如何,这里的关键点是规则是“不可清”; “可变类型(通过值而不是通过对象标识进行比较)”只是为了进一步解释事物。通过对象标识进行比较并按对象标识进行散列并不完全相同(唯一需要的是,如果id相等,则散列相等)。
从您发布的版本中“有效实现字典”的部分只会增加混淆(这可能是为什么它不在参考文档中)。即使有人提出了一种有效的方法来处理明天将列表存储为dict键,但语言不允许这样做。
答案 2 :(得分:0)
哈希是计算对象的唯一代码的方法,对于同一对象,此代码始终相同。例如hash('test')
是2314058222102390712
,因此是''test'; hash(a)
= 2314058222102390712
。
在内部,字典值由散列搜索,而不是由您指定的变量搜索。列表是可变的,列表的哈希值(如果定义的话)在列表更改时会发生变化。因此python的设计不会哈希列表。因此列表不能用作字典键。
元组是不可变的,因此tubles有哈希e.G. hash((1,2))
= 3713081631934410656
。可以通过比较散列而不是值来比较元组a
是否等于元组(1,2)
。这样会更有效率,因为我们只需要比较一个值而不是两个值。