推导式(comprehension)是 Python 里最有辨识度的语法糖。当你在别人的代码里看到 [x*2 for x in nums if x > 0] 这种写法,第一反应是”哇,好 Pythonic”。这一篇要把推导式从最基础的列表推导式讲到字典推导、集合推导,以及”看似很像但性能差很多”的生成表达式。学完这一篇,你写的代码会立刻精简 30%。
一、为什么要有推导式
先看不用推导式的写法:
1 2 3
squares = [] for x inrange(10): squares.append(x * x)
用列表推导式:
1
squares = [x * x for x inrange(10)]
好处:
简洁:一行搞定;
明确:[...] 一眼看出是列表;
更快:CPython 里推导式比 append 循环快 20%-50%。
二、列表推导式的语法
1
[表达式 for 变量 in 可迭代对象 if 条件]
表达式:每次迭代的输出;
for 子句:数据源,可以有多个;
if 子句:过滤,可选,可以有多个。
基本形式
1 2 3 4 5 6 7 8
# 平方 [x*x for x inrange(5)] # [0, 1, 4, 9, 16]
# 过滤:只要正数 [x for x in [-2, -1, 0, 1, 2] if x > 0] # [1, 2]
# 表达式可以是函数调用 [str(x).zfill(3) for x inrange(5)] # ['000', '001', '002', '003', '004']
多个 if
1 2 3 4
# 既大于 0 又是偶数 [x for x in nums if x > 0if x % 2 == 0] # 等价于 [x for x in nums if x > 0and x % 2 == 0]
if-else 在表达式部分
1 2
# 正数保留,负数变 0 [x if x > 0else0for x in nums]
注意语法位置:
过滤:if 在 for 后面;
三元:if-else 在表达式部分,for 前面。
多层 for
1 2 3 4 5 6 7 8
# 所有 (i, j) 对 [(i, j) for i inrange(3) for j inrange(3)] # [(0, 0), (0, 1), (0, 2), (1, 0), ..., (2, 2)]
# 展平二维列表 matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flat = [x for row in matrix for x in row] # [1, 2, 3, 4, 5, 6, 7, 8, 9]
读法:从左到右读 for,就像嵌套 for 循环从外到里。
但不要超过 2 层,否则可读性崩塌,改回普通循环。
三、字典推导式
用大括号,表达式部分写成 key: value:
1 2 3 4 5 6 7 8 9 10 11 12 13
# 平方表 {n: n*n for n inrange(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 反转字典 inv = {v: k for k, v in d.items()}
# 过滤字典 adults = {name: age for name, age in people.items() if age >= 18}
# 从两个列表 zip 出字典 d = {k: v for k, v inzip(keys, values)} # 更简单:d = dict(zip(keys, values))
四、集合推导式
大括号加”没有冒号的表达式”:
1 2 3 4
{x % 3for x inrange(10)} # {0, 1, 2}
# 从文本抽取所有独立单词 {w.lower() for w in text.split() if w.isalpha()}
五、生成表达式
把中括号换成小括号,就是生成表达式:
1 2 3 4 5 6 7
gen = (x*x for x inrange(10)) print(gen) # <generator object ...> print(next(gen)) # 0 print(next(gen)) # 1
for x in gen: print(x)
看起来像列表推导式,但它不会立刻算出所有元素——只在你需要时才产生下一个。
什么时候用生成表达式
巨大数据、只遍历一次、不需要索引 时用生成器:
1 2 3 4 5
# ❌ 内存爆炸:一次性生成 1 亿个数字 sum([x*x for x inrange(10**8)])
# ✅ 内存友好:逐个产生 sum(x*x for x inrange(10**8))
在函数调用作为唯一参数时,括号可以省略:
1 2
sum(x*x for x inrange(10)) # 括号省略 max((x.age for x in users), default=0) # 有多个参数就不能省
logs = [ "2025-09-10 08:00:11 GET /index 200 1.2.3.4", "2025-09-10 08:00:12 GET /login 200 5.6.7.8", "2025-09-10 08:00:13 POST /login 401 1.2.3.4", "2025-09-10 08:00:14 GET /profile 500 1.2.3.4", "2025-09-10 08:00:15 GET /index 200 9.9.9.9", ]
# 1. 解析每一行为字段 parsed = [line.split() for line in logs]
# 2. 提取所有 IP ips = [row[-1] for row in parsed]
# 3. 去重 IP 集合 unique_ips = {row[-1] for row in parsed}
# 4. IP → 请求次数 from collections import Counter ip_counts = Counter(ips)
# 5. 只保留 5xx 错误的日志 errors = [row for row in parsed if row[3].startswith("5")]
# 6. 每个 IP 的成功率 attempts = Counter(row[-1] for row in parsed) successes = Counter(row[-1] for row in parsed if row[3] == "200") rates = {ip: successes[ip] / attempts[ip] for ip in attempts}