为什么在Python中字典的键必须是不可变的?

为什么在Python中字典的键必须是不可变的?

在Python中,字典是使用键-值(key-value)对存储数据的一种数据类型。字典的键必须是唯一的、不可变的对象,而值可以是任何对象。例如,我们可以使用以下代码创建一个简单的Python字典:

my_dict = {"apple": 1, "banana": 2, "orange": 3}

在这个字典中,"apple""banana""orange"是键,分别对应值123

从字典实现的角度来说,为什么在Python中字典的键必须是不可变的呢?本文将会对这个问题进行深入的探讨。

阅读更多:Python 教程

背景

在正式回答为什么Python中字典的键必须是不可变的之前,我们需要先了解字典的基本实现方式。

在Python中,字典的内部实现是一个哈希表(Hash Table)。哈希表是一种通过将输入的键值(key value)作为哈希函数的输入来直接访问可存储值的数据结构。这样可以实现常数时间的搜索和插入操作,因此哈希表是高效的数据结构。

哈希表本质上是一个数组,数组的每个元素都是一个链表的头节点。每个链表包含了一组键-值对,这些键-值对根据哈希值分配到不同的链表中。假设我们要查找一个键的值,只需使用相应的哈希函数计算出该键的哈希值,并在具有相同哈希值的链表中查找该键的值。

从这个实现方式来看,字典的键必须是不可变的,这是因为字典的哈希值需要满足以下性质:

  • 哈希值必须是可计算的:如果键的哈希值不是固定的,则无法对其进行查找、插入或删除操作。因此,必须使用键的哈希值来将键映射到哈希表中的位置。

  • 哈希值必须是唯一的:如果两个不同的键具有相同的哈希值,则无法将它们分配到不同的链表中。如果这种情况发生了,查找操作将不能保证正确性。

尤其是哈希值需要固定且唯一,而对于可变对象,一旦发生改变,哈希值也会发生改变,因而无法实现对可变对象的哈希。

因此,在Python中,为了保证字典的正确性,采用了如下措施:

  • 字典的键必须是不可变的:因为只有不可变的对象才能保证哈希值的计算不发生变化。

  • 字典的值可以是任何对象:值可以是任何对象,包括可变对象。

为什么数字、字符串、元组等是不可变的?

在Python中,数字、字符串、元组等都是不可变的,这是因为这些类型的值不允许被修改,即它们是没有改变的可能。

对于数字,每个数字都是唯一的。例如,12是不同的数字,在内存中具有不同的存储位置。由于数字是不可变的,因此它们的哈希值可以根据其存储位置计算。因此,数字是字典中最常用的键类型之一。

对于字符串,它们的内容是固定的,因为字符串是不可变序列。如果字符串可以被修改,则会破坏哈希值和字符串的内容之间的关系,从而影响字典的正确性。因此,Python将字符串定义为不可变的。

元组是由一些值组成的序列,也是不可变的对象。元组的哈希值是通过对每个元素的哈希值进行计算得到的,因此如果元组中有一个元素被修改,则无法保证它的哈希值不变,从而无法将元组作为字典中的键。

总之,不可变对象的好处是它们可以被安全地用作字典的键,因为它们的哈希值是固定的且不受任何修改的影响。

可变对象为什么不能作为字典的键?

我们已经知道,字典的键必须是不可变的,并且为什么某些类型的对象是不可变的。那么为什么可变对象不能用作字典的键呢?下面我们将具体分析这个问题。

考虑一个可变对象列表my_list和一个字典my_dict。现在我们尝试将my_list作为字典的键,如下所示:

my_list = [1, 2, 3]
my_dict = {my_list: "value"}

尝试将my_list作为字典的键会导致一个TypeError异常:

TypeError: unhashable type: 'list'

这是因为my_list是一个可变对象,它的哈希值是可变的。如果对my_list进行修改,则它的哈希值也会随之更改。在这种情况下,Python无法保证字典操作的正确性。

这是一个例子,说明了为什么可变对象不能作为字典的键。实际上,对于任何可变对象,我们都无法保证它的哈希值是唯一且不变的,因此无法将其用作字典的键。

如何将可变对象作为字典的键?

在某些情况下,我们可能确实需要使用可变对象作为字典的键。例如,假设我们需要一个统计每个人喜欢的水果的字典。人的名称是字符串类型,但每个人喜欢的水果列表是可变类型。我们该如何处理这种情况?

一种方法是使用一个包含字符串和元组的复合键。元组用于存储每个人喜欢的水果列表。例如:

my_dict = {("Alice", ("apple", "banana", "orange")),
           ("Bob", ("mango", "banana")),
           ("Charlie", ("banana", "orange"))}

在这个例子中,字典的键是一个包含人名和喜欢的水果元组的键。元组是不可变的,所以可以被安全地用作字典的键。

当然,这种方法并非总是可行的。有时我们确实需要使用可变对象作为字典的键。在这种情况下,必须使用其他数据结构,例如列表、集合或元组,来存储键,并在需要时使用这些键。这种方法非常繁琐,而且对于大型字典非常不实用。

结论

我们已经详细地讨论了为什么在Python中字典的键必须是不可变的,特别是为什么各种类型的不可变对象(如数字、字符串、元组等)可以安全地用作字典的键。

我们还介绍了可变对象为什么不能用作字典的键,以及如何克服这种限制。

正如我们所看到的,Python的字典实现方式为哈希表。为了保证字典操作的正确性,Python需要使用哈希值来将键映射到哈希表的位置。因此,字典的键必须是不可变的对象,可以保证哈希值不变。

对于可变对象,它们的哈希值是可变的。如果使用可变对象作为字典的键,则无法保证哈希值的唯一性。因此,Python将可变对象定义为不可哈希的,即不能作为字典的键。

总之,在Python中,对于需要使用字典的场景,应该尽量使用不可变的对象作为键。如果需要使用可变对象作为键,则必须使用其他数据结构来代替字典。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程