这是什么意思?
唯一不能作为字典键接受的值类型是包含列表或字典或其他可变类型的值,它们按值而不是对象标识进行比较,原因是字典的有效实现需要键的哈希值保持不变。
我认为即使对于元组,比较也会按值进行。
这是什么意思?
唯一不能作为字典键接受的值类型是包含列表或字典或其他可变类型的值,它们按值而不是对象标识进行比较,原因是字典的有效实现需要键的哈希值保持不变。
我认为即使对于元组,比较也会按值进行。
将可变对象作为键的问题在于,当我们使用字典时,我们很少想要检查身份。例如,当我们使用这样的字典时:
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 不允许使用这些特定的可变键以避免微妙但常见的错误,即人们期望值等同于值,而不是身份。
该文档将两种不同的东西混合在一起:可变性和价值可比性。让我们把它们分开。
按身份比较的不可变对象很好。对于任何对象,身份永远不会改变。
按值比较的不可变对象很好。对于不可变对象,该值永远不会改变。这包括元组。
按身份比较的可变对象很好。对于任何对象,身份永远不会改变。
按值比较的可变对象是不可接受的。可变对象的值可以更改,这会使字典无效。
同时,您的措辞与 Mapping Types (Python 3.3 中的 4.10或Python 2.7 中的 5.8,两者都说:
字典的键几乎是任意值。不可散列的值,即包含列表、字典或其他可变类型的值(按值而不是对象标识进行比较)不能用作键。
无论如何,这里的重点是规则是“不可散列的”;“可变类型(通过值而不是对象身份进行比较)”只是为了进一步解释事情。严格来说,按对象身份进行比较和按对象身份进行散列总是相同的(唯一需要的是如果 id 相等,则散列相等)。
您发布的版本中有关“字典的有效实现”的部分只会增加混乱(这可能是它不在参考文档中的原因)。即使明天有人想出了一种有效的方法来处理将列表存储为 dict 键,该语言也不允许这样做。
哈希是计算对象唯一代码的方法,对于同一对象,此代码始终相同。hash('test')
例如 is 2314058222102390712
, a = 'test'; hash(a)
= 2314058222102390712
。
在内部,字典值是通过哈希而不是您指定的变量来搜索的。一个列表是可变的,一个列表的散列,如果它在哪里被定义,它会随着列表的改变而改变。因此python的设计没有散列列表。因此,列表不能用作字典键。
元组是不可变的,因此元组具有哈希 eG hash((1,2))
= 3713081631934410656
。可以通过比较哈希而不是值来比较元组a
是否等于元组。(1,2)
这会更有效率,因为我们只需要比较一个值而不是两个值。