Python推导式、迭代器和生成器的对比
一、形象比喻(快速理解)
- 推导式:“批量生产 + 装满货箱”
推导式本质是 “一次性生产所有元素,直接装满货箱(容器:列表 / 字典 / 集合)”:
你告诉工厂 “按规则生产 100 个零件”,工厂会立刻把 100 个零件全做出来,装进货箱(比如列表)里交给你;
货箱里的元素是实实在在存在的,占用内存,你可以随时开箱拿任意一个(比如list[5]);
就像你买一箱苹果,商家直接给你装满一箱,你当场就能数、能挑。
比如列表推导式[x*2 for x in range(5)],会直接生成[0,2,4,6,8]这个 “装满的货箱”。
- 迭代器:“按单取货(货箱已备好)”
迭代器是 “货箱已经装满,但你一次只取一个,取完为止”:
货箱里的元素早就存在(比如列表转的迭代器iter([0,2,4,6,8])),只是你不能直接翻箱,必须 “喊一声取一个”(next());
取走一个就少一个,不能回头取已经拿过的(比如取了第 3 个,就不能再拿第 2 个);
像自动售货机,货道里的饮料早就备好,你按一次出一瓶,直到卖完。
- 生成器:“按需生产 + 现做现送”
生成器是 “没有现成货箱,客户要一个才生产一个,生产完直接送走,不囤货”:
工厂里没有成品,你要一个零件,工厂才临时做一个给你,做完不存,接着做下一个;
完全不占用 “囤货的内存”,哪怕要生产 100 万个,也只在生产当下占用一点点资源;
像现做现卖的奶茶店,你点一杯才做一杯,做完直接给你,店里不囤成品奶茶。
二、核心知识点总结
1. 推导式
本质:用简洁语法批量生成容器(列表 / 字典 / 集合)的工具,一次性生成所有元素并存储。
(1)类型与操作
| 类型 |
语法 |
示例 |
| 列表推导式 |
[表达式 for 变量 in 可迭代 if 条件] |
[x*2 for x in range(5) if x%2==0] → [0, 4, 8] |
| 字典推导式 |
{键:值 for 变量 in 可迭代 if 条件} |
{x:x*2 for x in range(5)} → {0:0, 1:2, 2:4, 3:6, 4:8} |
| 集合推导式 |
{表达式 for 变量 in 可迭代 if 条件} |
{x*2 for x in range(5)} → {0, 2, 4, 6, 8} |
(2)注意点
⚠️ 一次性生成所有元素,占用内存与元素数量正相关(不适合超大数据集,比如生成 100 万元素的列表会直接占满内存);
✅ 支持嵌套循环(如[x for row in [[1,2],[3,4]] for x in row]展平矩阵),但嵌套超过 2 层可读性会暴跌;
❌ 仅用于 “生成容器”,无法直接嵌入打印、延迟等动作(需结合普通循环)。
2. 迭代器
本质:实现了__iter__()和__next__()方法的对象,按需逐个返回元素(不提前存储)。
(1)核心操作
# 创建迭代器:将可迭代对象(列表/字符串)转为迭代器
my_iter = iter([1, 2, 3])
# 取值:调用next()逐个获取(惰性!)
print(next(my_iter)) # 1 (首次计算)
print(next(my_iter)) # 2 (第二次计算)
print(next(my_iter)) # 3 (第三次计算)
# next(my_iter) # ❌ 无元素时抛StopIteration异常
(2)注意点
✅ 惰性取值:仅在调用next()时返回元素,节省内存(哪怕是 100 万元素的列表转迭代器,初始内存占用几乎为 0);
⚠️ 一次性遍历:元素取出后无法回溯(比如取了第 3 个元素,就不能再拿第 2 个,需重新创建迭代器);
❗ 可迭代对象(如列表[1,2,3])≠ 迭代器:可迭代对象需通过iter()转为迭代器才能用next()。
3. 生成器
本质:特殊的迭代器(自动实现迭代器协议),通过yield或推导式创建,按需生成元素。
(1)类型与操作
① 生成器推导式
gen = (x*2 for x in range(5)) # 生成器对象(初始无任何元素)
print(next(gen)) # 0 (按需生成)
print(next(gen)) # 2 (按需生成)
② 生成器函数(带yield)
def my_generator():
for i in range(3):
yield i*2 # 暂停函数,返回值;下次next()从这里继续
gen = my_generator()
print(next(gen)) # 0
print(next(gen)) # 2
print(next(gen)) # 4
(2)注意点
✅ 极致惰性:yield暂停函数执行,下次next()才继续生成(真正 “现做现卖”,无限序列也能处理,比如(x for x in itertools.count()));
⚠️ 同样一次性遍历:生成器耗尽后无法复用(需重新调用生成器函数 / 推导式);
✨ 高级操作:生成器函数可通过send()传值(双向通信),比如:
def gen():
while True:
x = yield
yield x*2
g = gen()
next(g) # 启动生成器
print(g.send(3)) # 6 (传入3,返回3*2)
三、三者核心区别对比
| 特性 |
推导式 |
迭代器 |
生成器 |
| 元素存储 |
📦 一次性存储所有元素 |
🚫 不存储元素(按需取) |
🚫 不存储元素(按需生成) |
| 内存占用 |
🚨 高(与元素数量相关) |
🟢 低(仅存状态) |
🟢 极低(零库存) |
| 遍历特性 |
🔄 可重复遍历 |
⚡ 一次性遍历 |
⚡ 一次性遍历 |
| 适用场景 |
小数据集生成容器 |
按需读取已有数据 |
动态生成数据(超大 / 无限序列) |