Python 系列(五十二) 掌握字典推导式,让字典创建更简单
1 | 作者:李晓辉 |
- Python 系列(一) 从零开始认识 Python,入门其实没那么难
- Python 系列(二) 学 Python 的第一步,把开发环境搭建好
- Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
- Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
- Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
- Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
- Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
- Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
- Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
- Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
- Python 系列(十一) list、tuple、set、dict,到底该怎么选?
- Python 系列(十二) List 列表实战,搞定数据增删改查与批量处理
- Python 系列(十三) Tuple 元组实战,搞懂不可变特性与 enumerate() 遍历
- Python 系列(十四) Set 集合实战,搞定数据去重与集合运算
- Python 系列(十五) Dict 字典详解,掌握快速查找与键值操作
- Python 系列(十六) sort() 和 sorted() 详解,掌握数据排序技巧
- Python 系列(十七) 自定义函数入门,让重复代码一次编写多次使用
- Python 系列(十八) 函数参数详解,搞懂位置参数、关键字参数和默认参数
- Python 系列(十九) 从 *args 到 **kwargs,让函数调用更灵活
- Python 系列(二十) 变量作用域是什么?一文搞懂 global 和 nonlocal
- Python 系列(二十一) 搞懂变量、对象和引用,理解函数参数传递机制
- Python 系列(二十二) map() 和 filter() 详解,轻松实现批量数据处理与筛选
- Python 系列(二十三) 嵌套函数与 lambda 表达式详解,搞懂作用域与闭包
- Python 系列(二十四) 搞懂递归函数,从基线条件到实际应用
- Python 系列(二十五) 聊聊模块与 import,让代码告别重复编写
- Python 系列(二十六) Python 模块入门,从 import 到模块复用
- Python 系列(二十七) Python dir() 函数详解,快速查看模块中的函数与属性
- Python 系列(二十八) 不会用 Python 标准库?教你快速查找模块和函数
- Python 系列(二十九) 搞懂 Python sys 模块,让脚本更适合自动化运维
- Python 系列(三十) 为什么 0.1 + 0.2 不等于 0.3?彻底搞懂 Decimal
- Python 系列(三十一) 搞懂 datetime 日期时间,轻松处理时区与时间计算
- Python 系列(三十二) 面向对象编程入门,从零理解类、对象、封装与继承
- Python 系列(三十三) 什么是类和对象?零基础理解 Python class
- Python 系列(三十四) Python 属性详解,搞懂类属性、实例属性和 setter
- Python 系列(三十五) 学会特殊方法,让对象打印出你想要的内容
- Python 系列(三十六) 多个对象如何共享数据?彻底搞懂类变量
- Python 系列(三十七) 搞懂父类、子类和方法重写,真正理解继承与多态
- Python 系列(三十八) 如何判断数据类型?三个内置函数一次讲明白
- Python 系列(三十九) 搞懂异常模型与常见异常类型,读懂程序报错
- Python 系列(四十) 从零学会异常处理,掌握 try、except、else 和 finally
- Python 系列(四十一) ValueError、TypeError 到底是什么?一文搞懂常见异常
- Python 系列(四十二) ValueError、TypeError 到底是什么?一文搞懂常见异常
- Python 系列(四十三) 别只会处理异常!学会自定义异常,让程序更容易维护
- Python 系列(四十四) 从零认识 assert 断言,让程序学会检查自己
- Python 系列(四十五) 使用 sys 模块处理标准输入与输出
- Python 系列(四十六) 使用 open() 读取和写入文件
- Python 系列(四十七) 学会读写文本文件,让程序保存运行结果
- Python 系列(四十八) 搞懂 bytes、bytearray 和二进制读写
- Python 系列(四十九) 使用 seek() 和 tell() 灵活控制文件读取位置
- Python 系列(五十) 掌握 os 和 os.path,实现文件与目录自动化管理
- Python 系列(五十一) 掌握列表推导式,轻松实现列表生成与数据筛选
- Python 系列(五十二) 掌握字典推导式,让字典创建更简单
- Python 系列(五十三) 从零理解生成器,轻松处理大量数据
- Python 系列(五十五) 使用 zip() 同时遍历多个集合
前面我们学习了列表推导式,知道了可以通过一行代码,根据已有的数据快速创建一个列表。
例如,我们有一组数字,希望把它们转换成十六进制字符串,再保存到列表中,就可以使用列表推导式:
1 | numbers = [10, 20, 30, 40] |
运行结果:
1 | ['0xa', '0x14', '0x1e', '0x28'] |
不过,在实际工作中,我们并不总是需要列表。
例如,假设我们正在管理 Linux 服务器,需要记录每台服务器的名称和 IP 地址:
1 | servers = [ |
现在,这些数据保存在列表中,每台服务器的信息由一个元组记录。
如果希望通过服务器名称快速查找对应的 IP 地址,那么使用字典会更加方便。
我们可以手动创建一个字典:
1 | server_ips = { |
运行结果:
1 | 192.168.1.10 |
这种方式没有问题。
但是,如果服务器有几百台甚至几千台,数据又已经保存在列表中,我们难道还要手动把每台服务器的信息重新写进字典吗?
有没有一种方法,能够直接利用现有数据,自动生成我们需要的字典?
这就是今天要学习的内容:字典推导式。
什么是字典推导式
字典推导式是一种根据已有数据,快速创建字典的简洁写法。
它和列表推导式的思路非常相似,只不过最终创建出来的数据结构不同。
- 列表推导式创建列表,使用方括号
[]。 - 字典推导式创建字典,使用花括号
{},并且每一项都需要包含键和值。
先来看一个简单的例子:
1 | numbers = [1, 2, 3, 4] |
运行结果:
1 | {1: 1, 2: 4, 3: 9, 4: 16} |
这个结果可能有点陌生,我们一步一步理解。
原始数据是一个列表:
1 | numbers = [1, 2, 3, 4] |
我们希望把每个数字作为字典的键,再把这个数字的平方作为对应的值。
最终得到的字典,相当于:
1 | number_dict = { |
例如,数字 3 对应的键是 3,对应的值是 9,因为 (3 \times 3 = 9)。
字典推导式就是把这个创建过程简洁地写在一行代码中。
字典推导式的基本语法
字典推导式的基本语法如下:
1 | {键表达式: 值表达式 for 变量 in 可迭代对象} |
这里有几个部分需要理解。
| 语法部分 | 含义 |
|---|---|
{} | 表示创建一个字典 |
键表达式 | 决定字典中每个项目的键 |
值表达式 | 决定字典中每个项目的值 |
for 变量 in 可迭代对象 | 依次取出数据,逐项生成字典内容 |
其中,所谓的可迭代对象,可以先简单理解为能够逐个取出数据的对象,例如列表、元组和字符串。
例如:
1 | numbers = [1, 2, 3] |
运行结果:
1 | {1: 10, 2: 20, 3: 30} |
我们可以把这行代码拆开理解:
1 | {number: number * 10 for number in numbers} |
for number in numbers:依次取出列表中的每个数字。number:把当前数字作为字典的键。number * 10:把当前数字乘以 10,作为字典的值。{}:将生成的键和值组合起来,创建字典。
需要注意,number 只是我们自己起的变量名,也可以换成其他容易理解的名字。
理解字典推导式的执行过程
对于刚接触 Python 的朋友来说,直接看一行代码可能不容易理解。
我们可以把它展开,看看 Python 是如何逐项创建字典的。
还是使用刚才的例子:
1 | numbers = [1, 2, 3] |
它的执行过程可以理解为:
flowchart TD
A["开始创建字典"] --> B["取出第一个数字 1"]
B --> C["计算键和值:1 : 10"]
C --> D["保存到字典"]
D --> E["取出下一个数字"]
E --> F{"还有数字吗?"}
F -->|有| G["计算当前数字对应的键和值"]
G --> D
F -->|没有| H["得到最终字典"]整个过程可以分为以下几步:
第一步:取出数字 1
Python 取出第一个数字 1。
第二步:计算键和值
键是 number,因此键为 1。
值是 number * 10,因此值为 10。
字典中就有了这样一个项目:
1 | 1: 10 |
第三步:继续处理其他数字
接下来,Python 依次处理数字 2 和 3,分别生成:
1 | 2: 20 |
第四步:得到完整字典
最终生成:
1 | {1: 10, 2: 20, 3: 30} |
这就是字典推导式的基本执行过程:取出数据、计算键和值、将结果放入字典,直到所有数据处理完成。
使用字典推导式转换已有数据
理解基本语法后,我们再来看一个更贴近实际工作的例子。
假设我们管理着几台 Linux 服务器,已经将服务器名称和 IP 地址保存到了一个列表中:
1 | servers = [ |
每个元组中,第一个元素是服务器名称,第二个元素是 IP 地址。
现在,我们希望创建一个字典,让服务器名称作为键,IP 地址作为值。
可以这样写:
1 | servers = [ |
运行结果:
1 | { |
这里出现了一个新的写法:
1 | for name, ip in servers |
为什么可以同时使用 name 和 ip 两个变量?
因为 servers 中的每个元素都是一个包含两个值的元组。
例如:
1 | ("web01", "192.168.1.10") |
Python 可以把这个元组中的两个值分别赋给两个变量:
1 | name = "web01" |
这种操作叫作序列解包。这里不需要急着深入研究,只要知道两个变量可以分别接收元组中的两个值即可。
接下来:
1 | name: ip |
表示将服务器名称作为字典的键,将 IP 地址作为字典的值。
因此,最终创建出来的字典可以通过服务器名称查找 IP 地址:
1 | print(server_ips["web01"]) |
运行结果:
1 | 192.168.1.10 |
这就是字典推导式的一个实际用途:将已有的数据转换成更方便查询的字典结构。
在字典推导式中添加条件
有时候,我们并不希望所有数据都进入字典。
例如,服务器清单中可能同时包含生产服务器和测试服务器,但我们只想提取名称以 web 开头的服务器。
字典推导式也可以添加条件。
基本语法如下:
1 | {键表达式: 值表达式 for 变量 in 可迭代对象 if 条件} |
这里的 if 用来判断当前数据是否满足条件。
满足条件的数据才会进入最终字典,不满足条件的数据会被跳过。
来看一个例子:
1 | servers = [ |
运行结果:
1 | { |
其中:
1 | if name.startswith("web") |
表示检查服务器名称是否以 web 开头。
例如:
1 | "web01".startswith("web") |
结果是:
1 | True |
而:
1 | "db01".startswith("web") |
结果是:
1 | False |
因此,web01、web02 和 web03 会进入字典,而 db01 会被跳过。
我们可以把整个过程理解为:
flowchart TD
A["读取服务器信息"] --> B["获取服务器名称和 IP"]
B --> C{"名称是否以 web 开头?"}
C -->|是| D["将名称和 IP 加入字典"]
C -->|否| E["跳过当前服务器"]
D --> F{"还有服务器吗?"}
E --> F
F -->|有| B
F -->|没有| G["得到筛选后的字典"]这里需要注意,if 放在 for 后面,表示筛选数据。
它并不是让整个字典创建过程只执行一次,而是对每一条数据分别进行判断。
使用字典推导式调整字典中的值
除了创建新字典和筛选数据,字典推导式还可以根据已有字典生成一个新字典,并修改其中的值。
例如,我们有一份服务器 CPU 使用率清单:
1 | cpu_usage = { |
假设这些数字表示 CPU 使用率的百分比。
现在,我们希望把所有服务器的使用率转换成带有百分号的字符串,方便后续展示。
可以这样写:
1 | cpu_usage = { |
运行结果:
1 | { |
这里使用了:
1 | cpu_usage.items() |
它可以让我们逐项获取字典中的键和值。
例如,第一项可以理解为:
1 | ("web01", 35) |
于是:
1 | for name, usage in cpu_usage.items() |
就可以分别获取服务器名称和 CPU 使用率。
而:
1 | f"{usage}%" |
则使用 f-string 将数字和百分号组合成字符串。
例如,usage 的值是 35 时,结果就是字符串 "35%"。
这样,我们就根据原始字典创建了一个新的字典,而且没有修改原来的数据。
字典推导式与普通循环有什么区别
学到这里,你可能会问:如果字典推导式可以完成这些操作,那么普通的 for 循环是不是就没有用了?
当然不是。
字典推导式的优势在于,当需求比较简单时,可以用更简洁的代码完成字典创建。
例如,使用普通循环创建数字平方字典:
1 | numbers = [1, 2, 3, 4] |
运行结果:
1 | {1: 1, 2: 4, 3: 9, 4: 16} |
使用字典推导式,可以写成:
1 | numbers = [1, 2, 3, 4] |
运行结果相同。
两种方式的区别在于:
| 比较项目 | 普通 for 循环 | 字典推导式 |
|---|---|---|
| 代码长度 | 通常较长 | 通常较短 |
| 阅读方式 | 一步一步执行赋值 | 直接描述生成规则 |
| 适合场景 | 逻辑复杂、需要多步处理 | 根据数据快速生成字典 |
| 可读性 | 复杂逻辑下更容易组织 | 简单转换时通常更直观 |
对于初学者来说,不需要为了追求代码简短,就把所有循环都改成推导式。
如果逻辑比较简单,字典推导式可以让代码更加紧凑;如果需要多层判断、复杂计算或者多步操作,普通循环往往更容易理解和维护。
代码简洁很重要,但让别人看得懂同样重要。
使用字典推导式时需要注意什么
字典的键不能重复
字典中的键具有唯一性。
例如:
1 | numbers = [1, 1, 2, 2, 3] |
运行结果:
1 | {1: 10, 2: 20, 3: 30} |
虽然原始列表中有重复数字,但最终字典中只会保留每个键对应的一项。
如果同一个键生成了多次,后面生成的值会覆盖前面同一个键对应的值。
因此,使用字典推导式时,要留意生成的键是否可能重复。
字典推导式不一定让程序运行得更快
字典推导式主要是为了让代码更加简洁,表达数据转换的意图。
它不意味着所有情况下都会比普通循环运行得更快,也不能减少创建字典本身所需要的存储空间。
如果处理的是大量服务器信息、日志数据或者其他业务数据,除了代码写法,还需要考虑数据规模、处理逻辑和内存使用情况。
不要把代码写得过于复杂
例如,简单的数据转换可以直接使用字典推导式:
1 | numbers = [1, 2, 3] |
但如果一行代码中塞入多层循环、复杂条件和大量计算,就可能变得难以阅读。
遇到这种情况,可以先使用普通循环把逻辑写清楚,再考虑是否适合改写成推导式。
动手练习
掌握一个知识点,最好的办法就是自己动手写一遍。
下面有三个小练习,可以先尝试独立完成,再查看参考答案。
练习:创建数字平方字典
给定一个数字列表:
1 | numbers = [2, 4, 6, 8] |
请使用字典推导式,让每个数字作为键,它的平方作为值。
预期结果:
1 | {2: 4, 4: 16, 6: 36, 8: 64} |
参考答案:
1 | numbers = [2, 4, 6, 8] |
这里的 ** 2 表示求平方,例如 4 ** 2 的结果是 16。
练习:筛选指定范围内的数字
给定一个数字列表:
1 | numbers = [10, 25, 40, 55, 70] |
请创建一个字典,只保留大于或等于 40 的数字,并让每个数字作为键、数字乘以 2 的结果作为值。
预期结果:
1 | {40: 80, 55: 110, 70: 140} |
参考答案:
1 | numbers = [10, 25, 40, 55, 70] |
这里的 if number >= 40 用于筛选数据,只有满足条件的数字才会进入字典。
练习:生成服务器 IP 地址字典
给定服务器信息:
1 | servers = [ |
请使用字典推导式,创建一个只包含 web 开头的服务器名称及其 IP 地址的字典。
预期结果:
1 | { |
参考答案:
1 | servers = [ |
这个练习结合了字典创建、序列解包和条件筛选,也是字典推导式在实际数据处理中的一种常见用法。
写在最后
今天我们学习了字典推导式,了解了如何利用已有数据快速创建字典,以及如何通过条件筛选数据、转换字典中的值。
回顾一下,字典推导式的基本语法是:
1 | {键表达式: 值表达式 for 变量 in 可迭代对象} |
如果需要筛选数据,可以在后面添加 if 条件:
1 | {键表达式: 值表达式 for 变量 in 可迭代对象 if 条件} |
记住这两个基本形式,再结合实际需求练习,就能逐渐掌握字典推导式。
从列表推导式到字典推导式,我们已经接触了两种使用简洁语法创建数据结构的方式。
不过,还有一个问题值得思考:如果数据量非常大,我们暂时不需要一次性创建并保存所有结果,而是希望程序用到一个数据时,再生成一个数据,该怎么办?
例如,处理大型日志文件、逐条读取服务器监控数据时,我们有时并不需要提前把所有结果放进列表中。
下一篇,我们将学习 生成器,看看 Python 如何在需要时逐个生成数据,帮助我们更灵活地处理数据。
