Python字典详解:基石作用、实现原理与键值索引特性
字典是 Python 的基础。这门语言的诸多方面都是以字典为基础构建起来的。

模块是与 Python 实现紧密联系且为字典的例子;类是与 Python 实现紧密联系且为字典的例子;对象是与 Python 实现紧密联系且为字典的例子;globals()是与 Python 实现紧密联系且为字典的例子;locals()是与 Python 实现紧密联系且为字典的例子。
以下是 Python 官方文档定义字典的方式:
需要注意的是:
序列支持使用整数索引进行元素访问,它是可迭代对象。而字典是按键编制索引的。这表明我们能够利用关联的键,而非整数索引,去访问存储在字典里的值。
字典中的键类似于 set 。它是由可哈希且唯一的对象所组成的集合。因为键需要进行可哈希的处理,所以不能把可变对象当作字典的键,也就是说键不能是可变的数据类型。
一方面,Python 字典的值可以是任何类型。这些类型不论是否可哈希都可以。从字面意义来讲,对值不存在任何限制。我们能够将任何数据类型当作 Python 字典中的值来使用。
在 Python 3.6 之前,字典属于无序的数据结构。这表明 item 的顺序通常和插入顺序不一致。
保持 item 有序是很有用的功能。然而,若所使用的代码支持较旧的 Python 版本,就不能依靠此功能,因为可能会产生 bug。而对于较新的版本,依赖该特性则是完全安全的。
字典的一个重要特征在于它们属于可变的数据类型。这表明我们能够依据需求在原地对其项目进行添加、删除以及更新的操作。
值得注意的是,这种可变性意味着不能把字典当作另一个字典里的键。
如何在 python 中遍历字典
Python 开发人员时常会碰到这样的情形:当对其键值对进行某些操作的时候,需要对现有的字典进行遍历。
因此,了解 Python 中字典迭代的不同方式是很重要的。保持 item 的有序性是一个很有用的功能。
Python 的字典具备一些特殊的方法,这些方法在 Python 内部被用于执行一些操作。
这两个方法的命名约定为,在方法名的开头添加两个下划线,并且在方法名的末尾也添加两个下划线。
可以通过使用内置的 dir() 函数来获取任何 Python 对象所提供的方法和属性的列表。当使用空字典作为参数来运行 dir() 函数时,就能够获得 dict 类的所有方法和属性。
可以看到“__iter__”这个属性,它是 Python 在需要容器数据类型的迭代器时会自动调用的一种方法。
该方法需返回一个新的迭代器对象,此对象能让我们对底层容器类型里的所有项进行遍历。
Python 字典在默认情况下允许通过.__iter__() 来直接迭代键。若你在 for 循环中直接使用字典,Python 会自动调用.__iter__() 属性,这样你就能获得一个遍历其键的迭代器。
Python 很聪明,它知晓 likes 是一个字典,并且实现了.__iter__()。在该例子里,Python 会自动调用.__iter__(),这使得能够对 likes 字典的键进行迭代。
在 Python 中,遍历字典有一个主要的方法。你只需要将字典直接放入一个 for 循环里。
如果使用此方法,并且与 [key] 运算符一同使用,那么在循环访问键的时候,就能够访问到字典的值。
在这个例子里,用 key 来进行访问目标字典的键,同时用 likes[key] 来访问目标字典的值
在 Python 中直接遍历字典是很简单的。然而,字典提供了一些更方便且更明确的工具,能够让我们获得相同的结果。
.items() 这个方法处于这种状况,它规定了一种能够快速对字典的 item 或者键值对进行迭代的方式。
使用字典时,一个常见要求可能是同时循环访问键和值。.items() 方法能返回一个视图对象,此对象包含着字典的项,并且这些项是以键值元组的形式存在的。
字典视图对象能够提供字典项的一种动态的视图。在这种情况下,“动态”意味着当字典发生更改时,该视图会随之反映出这些更改。
视图是可以进行迭代操作的。所以,我们能够利用调用.items() 所生成的视图对象,来循环遍历字典中的各项。以下示例便可展示这一过程:
在此示例中,会返回一个视图对象,这个对象能够一次生成一个键值对,通过使用.items()方法,还允许我们对这些键值对进行循环访问。
仔细观察所产生的各个项目.items(),就会注意到这些项目是 tuple 对象。
能够看到所有的 item 均为元组。当知晓这一情况后,便可以运用元组解包的方式来同时遍历键与值。
要实现并行迭代并通过键和值来进行,只需把每个 item 的元素进行解压缩。将其解压缩为两个不同的变量,一个用来表示键,另一个用来表示值。
在 for 循环头中,对 key 和 value 变量进行解包操作。每次循环运行时,key 会获取到对当前键的引用,而 value 会获取到对值的引用。
这样,我们能够更好地掌控字典的内容。所以,我们能够以可读的方式以及以 Python 的方式分别对键和值进行处理。
Python 字典提供了第二种遍历其键的方式。一种方式是在循环中直接使用目标字典,另一种方式是使用.keys()方法。
这个方法返回一个只包含字典键的视图对象
该方法的.keys() 能够返回一个对象,此对象能提供 likes 键的动态视图。利用这个视图对象,就可以对字典键进行循环访问。
当您在“likes”上调用“.keys()”这个操作时,您会获得键的视图。因为 Python 知晓视图对象是可迭代的,所以它就开始进行循环。
为什么要使用.keys()呢?为什么不直接遍历字典呢?简单来讲,显式地运用.keys()能够让你更清晰地表达出只遍历键的这种意图。
在遍历字典时,另一个常见的需求是仅遍历值。实现这个需求的方法是使用.values()方法,此方法会返回一个包含底层字典中值的视图。
上面的代码会返回一个视图对象,并且这个视图对象通过.values()方法也能返回一个视图对象。
和其他视图对象一样,.values() 这个结果是可以进行迭代的,所以能够在循环当中被使用。
使用 .values() ,只能访问目标字典的值
有时在 Python 中迭代字典时需要更改字典中的值
在下面的例子中,你在一个字典中更新了一堆产品的价格:
在上面的例子中需注意:要更新值时,我们运用的是原始的字典,并非像“price = round(price * 0.9, 2)”那样直接对当前价格进行更新。
如果像“price = round(price * 0.9, 2)”这样做,重新分配水果或者调整价格的情况并没有在原本的字典里体现出来。
会致使丢失对字典的引用,并且并未达成更改字典中任何内容的目的
因为 Python 字典是可变的,所以我们能够依据需求从其中把已有的 item 删除掉。
在下面的示例中,我们根据项的特定值选择性地删除项
注意,为了在遍历字典时安全地缩小字典,我们需要使用一个副本
在这个例子里,通过使用.copy() 来生成目标字典 fruits 的浅拷贝。接着进行循环,在循环过程中遍历这个拷贝,与此同时从原始字典里移除项,在这个例子中,是运用 del 语句来删除字典项。
但是也可以使用 .pop() 将目标键作为参数
如果在尝试删除循环中的 item 时未使用目标词典的副本,那么就会收到错误。
在迭代过程中,当你试图从字典里删除一个 item 的时候,Python 会引发 RuntimeError
原始字典的大小发生了变化,所以对于如何继续迭代并不明确。因此,为避免这个问题,在迭代过程中应始终使用字典的副本。
遍历期间对字典的操作
有时候我们期望在原本的字典基础之上,创建出一个仅仅包含那些满足特定条件的新字典。
我们可以在遍历原字典的时候加上条件判断
在此示例里,把筛选值小于 2 的项目找出来,然后把这些项目添加到 small_numbers 字典里。
有一种技术能够用于从字典中过滤 item。其原因在于键的视图对象与 Python 集合对象相似。
它们支持集合操作,比如并集、交集以及差分。能够借助这种类似集合的行为,从字典里过滤某些键。
还可以更简洁
通过计算字典的键与一组不需要的键之间的差分,从而获得键集,再用这个键集构建一个新词典。
在遍历字典时,我们可以对字典的值进行计算
可以使用内置的 sum() 函数。将字典中的值当作参数直接传递给 sum() 以便进行求和操作。
我们可以在遍历的时候交换字典的键和值
更简洁的写法
需要注意的是,原始字典值中的数据必须是可哈希数据类型
我们能够把内置的 zip() 函数和 dict() 构造函数配合着使用。
在上面的示例里,先通过 zip() 来生成值键对的元组,接着,利用生成的这些元组当作参数,再通过 dict() 来构建所需要的字典。
字典推导式
与列表推导式不同,字典推导式需要一个映射到值的键
上面的对象中,zip()将两个可迭代对象(categories、objects)进行接收,从而生成了一个tuple对象。接着,这个tuple对象被解压缩到了key和value中。最终,它被用于创建新的所需字典。
更简洁的方法如下:
zip()函数会从原始列表中生成键值对,dict()构造函数则负责创建新的字典。