Python 从入门到精通
第 16 / 36 篇
从环境安装和基础语法出发,逐步学习工程实践、自动化、数据处理与 Web 开发。
-
01
Python 怎么装、怎么用 -
02
变量、数字和字符串 -
03
列表:把一组数据放在一起 -
04
元组、集合和字典 -
05
条件判断:让程序做选择 -
06
循环:重复的事交给程序 -
07
函数:把代码整理成可复用的块 -
08
模块与包:拆分你的程序 -
09
输入、输出与字符串格式化 -
10
文件读写:保存程序的数据 -
11
异常处理:程序出错时怎么办 -
12
基础阶段练习:命令行记账本 -
13
类和对象:面向对象入门 -
14
继承、组合与特殊方法 -
15
迭代器与生成器 -
16
列表推导式与生成器表达式正在阅读 -
17
装饰器:给函数增加能力 -
18
上下文管理器与 with -
19
类型标注与 dataclass -
20
正则表达式:从文本中找规律 -
21
日期、时间与时区 -
22
日志与调试 -
23
虚拟环境与依赖管理 -
24
测试:让修改不再提心吊胆 -
25
网络请求:用 Python 调用 API -
26
网页解析与合规采集 -
27
操作 Excel、CSV 与批量文件 -
28
SQLite:给程序加一个数据库 -
29
数据分析入门:NumPy 与 Pandas -
30
画图:把数据变得直观 -
31
Flask 入门:做一个小网站 -
32
异步编程:同时处理多项任务 -
33
线程、进程与并发选择 -
34
性能分析与优化 -
35
项目结构、配置与发布 -
36
综合项目:从需求到上线
写 Python 的人都知道一个说法:能用一行推导式解决的,别写三行循环。推导式(comprehension)是 Python 最优雅的语法之一,这一篇把列表、字典、集合三种推导式一次讲透,外加生成器表达式。
列表推导式:循环的压缩版
最常见的需求:把一个列表变换成另一个列表。传统写法 vs 推导式:
nums = [1, 2, 3, 4, 5]
# 传统写法
squares = []
for n in nums:
squares.append(n * n)
# 推导式(一行搞定)
squares = [n * n for n in nums]
print(squares) # [1, 4, 9, 16, 25]
结构拆解:[表达式 for 变量 in 可迭代对象]——把”要什么”写在前面,把”从哪来”写在后面。读法:对 nums 里每个 n,产出 n*n。
带条件的推导式
后面加 if 过滤:
nums = [1, 2, 3, 4, 5, 6, 7, 8]
# 只要偶数,再平方
evens_squared = [n * n for n in nums if n % 2 == 0]
print(evens_squared) # [4, 16, 36, 64]
# 实战:从一堆字符串里挑出长度大于 2 的
words = ["南山小站", "开张了", "好", "python"]
long_words = [w for w in words if len(w) > 2]
print(long_words) # ['南山小站', '开张了', 'python']
else 也要?用三元表达式
想”符合条件的变一个样,不符合的变另一个样”,把三元表达式放前面:
nums = [1, 2, 3, 4, 5]
# 偶数×10,奇数不变
result = [n * 10 if n % 2 == 0 else n for n in nums]
print(result) # [1, 20, 3, 40, 5]
注意顺序:[值 if 条件 else 其他值 for ...]——if/else 在表达式位置(前面),单独的 if 过滤在后面。位置别搞混。
嵌套循环的推导式
两层循环也能压成一行,但可读性要注意:
# 两个列表的笛卡尔积
colors = ["红", "蓝"]
sizes = ["S", "M"]
combos = [f"{c}{s}" for c in colors for s in sizes]
print(combos) # ['红S', '红M', '蓝S', '蓝M']
# 等价的三层写法
combos = []
for c in colors:
for s in sizes:
combos.append(f"{c}{s}")
写嵌套推导式时心里默念:for 的顺序和普通循环一样(外层先内层后)。超过两层就别用推导式了,可读性崩坏。
字典推导式
花括号 + 键值对:
# 数字 → 平方的字典
squares = {n: n * n for n in range(1, 6)}
print(squares) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 实战:统计字符串里每个字符出现次数
text = "hello"
counts = {ch: text.count(ch) for ch in set(text)}
print(counts) # {'h': 1, 'e': 1, 'l': 2, 'o': 1}
# 反转字典(键值互换)
d = {"a": 1, "b": 2}
reversed_d = {v: k for k, v in d.items()}
print(reversed_d) # {1: 'a', 2: 'b'}
集合推导式
同样的语法,花括号但没有冒号,自动去重:
nums = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {n * n for n in nums}
print(unique_squares) # {1, 4, 9, 16}(自动去重)
# 实战:提取所有句子里的唯一单词
sentences = ["hello world", "hello python"]
words = {w for s in sentences for w in s.split()}
print(words) # {'hello', 'world', 'python'}
生成器表达式:圆括号版
把方括号换成圆括号,就是生成器表达式——不立即计算,遍历时才算:
nums = range(100_000_000) # 1 亿个数
gen = (n * n for n in nums) # 几乎不占内存
print(sum(gen)) # 边算边加
# 对比:列表推导式会先造一个 1 亿元素的列表
# lst = [n * n for n in nums] # 内存爆炸,别试!
判断标准:只要遍历一次就用生成器表达式(sum/max/any/all 都是只遍历一次),要反复用、要下标才用列表。
什么时候别用推导式
推导式不是万能的,三条红线:
- 逻辑复杂时:超过两个 for 或条件很绕,写普通循环,可读性优先
- 要调试打印时:推导式里没法插 print,循环可以
- 有副作用时:推导式应该”纯计算”,别在里面做文件写入、网络请求
好代码的第一标准是别人(三个月后的你)能看懂,其次才是短。
新手坑
坑 1:if 位置放错。[n for n in nums if n > 2 else 0] 语法错误——带 else 的三元表达式必须放最前面。
坑 2:变量遮蔽。推导式里的循环变量会泄漏到外层作用域(Python 2 遗留),同名变量小心被覆盖。Python 3 里列表推导式不泄漏,但别依赖。
坑 3:为了短而短。一行 200 字符的推导式没人看得懂,拆开写。
小结
- 列表推导式
[expr for x in it],可加if过滤、三元表达式变形 - 字典推导式
{k: v for ...},集合推导式{x for ...}(自动去重) - 生成器表达式用圆括号,只遍历一次时用,省内存
- 嵌套 for 顺序和普通循环一致;超过两层就别硬写
- 可读性永远优先,复杂逻辑用普通循环
练习
- 用推导式把
["1", "2", "3"]转成整数列表[1, 2, 3](提示:int) - 用推导式从
range(1, 101)里筛出能被 3 整除但不能被 5 整除的数 - 思考:
[x for x in range(10)]和(x for x in range(10))有什么区别?什么时候必须用后者?