Slashrun
100.37M · 2026-04-09
在日常开发中,我们经常写 for 循环,却很少去思考它是如何工作的。本篇从迭代机制入手,再过渡到生成器。
先看一个常见的循环:
nums = [1, 2, 3]
for n in nums:
print(n)
这里的 nums 是可迭代对象(Iterable) ,它的特点是:可以被 for 遍历。而真正执行遍历的是迭代器 ( Iterator ) 。使用 iter() 把可迭代对象变成迭代器。
nums = [1, 2, 3]
it = iter(nums) # 把列表变成迭代器
# 每调用一次 next (it),就从迭代器里取一个值,指针往后走一步。
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# 取完了再取,就会报错 StopIteration(迭代结束)
总结一下关系:
iter() 转换成迭代器next() 一个个取值for 循环本质上就是一个“自动调用 next 的过程”:
nums = [1, 2, 3]
it = iter(nums)
while True:
try:
n = next(it)
print(n)
except StopIteration:
break
也就是说:
理解这一点后,再看生成器会更自然。
生成器的核心在于 yield,它会让函数“暂停执行”。你可以这么理解,yield = 带暂停功能的 return, 它和return的区别是:
return:函数直接结束yield:函数暂停在这里,下次继续从这行往下跑def count_up_to(n): # 定义函数,最多数到 n
i = 1 # 从 1 开始数
while i <= n: # 只要没超过 n,就一直循环
yield i # 暂停,返回当前的 i,记住位置
i += 1 # 下次被唤醒时,执行这行:i+1
调用时:
# 创建生成器(此时函数还没开始跑)
gen = count_up_to(3)
# 第一次 next() → 跑到 yield 1,暂停,返回 1
print(next(gen)) # 输出 1
# 第二次 next() → 从暂停处继续,i=2,yield 2,暂停
print(next(gen)) # 输出 2
# 第三次 next() → 继续,i=3,yield 3,暂停
print(next(gen)) # 输出 3
# 第四次 next() → 循环结束,函数退出 → 报错 StopIteration
print(next(gen))
本质上,生成器就是一种自动实现了 迭代器 协议的对象。即,Python 看到 yield,就自动在后台:
iternextStopIteration列表推导式 vs 生成器表达式:
# 列表推导式,瞬间全部算完,放进内存
nums = [x * x for x in range(5)]
# 生成器表达式,什么都不计算,通过next()取值
gen = (x * x for x in range(5))
使用方式对比:
# 列表:一次性生成好
nums = [x*x for x in range(5)]
print(nums) # [0, 1, 4, 9, 16]
print(nums[2]) # 4 可以直接索引
print(nums[3]) # 9 可以随便取
# 生成器:按需生成
gen = (x*x for x in range(5))
print(gen) # 打印的是一个生成器对象,不是数据
print(next(gen)) # 0 要一个,算一个
print(next(gen)) # 1
print(next(gen)) # 4
当数据 很大 / 很慢 / 只用一次 时,优先考虑生成器。
def read_big_file(path):
with open(path) as f:
for line in f:
yield line
如果改成:
lines = f.readlines()
当文件很大时,会一次性占用大量内存。生成器按行读取,可以把内存占用控制在一个稳定范围。
例如接口分页、消息队列、日志流:
def fetch_pages():
page = 1
while True:
data = request_api(page)
if not data:
break
yield from data
page += 1
特点是:
如果数据只会被消费一次(例如过滤日志、扫描文件),生成器更合适:
gen = (line for line in read_log("app.log") if "ERROR" in line)
相比列表,不会提前做无用计算。
| 概念 | 说明 |
|---|---|
| 可迭代对象 | 实现了 iter 的对象,可被 for 循环遍历 |
| 迭代器 | 经 iter() 转换后,支持 next() 逐步取值 |
| 生成器函数 | 含 yield 的函数,调用后返回生成器对象 |
| 生成器表达式 | (expr for x in iterable),圆括号版的列表推导式 |
| 核心优势 | 惰性求值,按需产出,内存占用与数据量无关 |