在 Python 中,有一种非常神奇的函数:生成器函数(Generator Function) 。它既像函数,又像一个"能记住中间状态"的魔法盒子,处理大数据流时特别好用!
本篇文章会一步一步带你搞懂生成器函数到底是个啥,有啥用,怎么写,怎么用。
参考文章:Python 迭代器 | 简单一点学习 easyeasy.me
目录
- 什么是生成器函数?
- 普通函数 vs 生成器函数
yield
是啥?怎么用?- 用
for
遍历生成器函数 yield
与return
的区别- 用
next()
手动调用生成器 yield from
:更优雅地嵌套生成器- 生成器函数的应用场景
- 注意事项与陷阱
- 小结和写法建议
1. 什么是生成器函数?
生成器函数,就是一种用 yield
而不是 return
返回值的函数 。它的作用是:每次调用返回一个值,但不会退出函数,而是暂停在当前位置,下一次继续从暂停处执行。
举个栗子:
python
def my_gen():
yield 1
yield 2
yield 3
调用这个函数时,它不会立马执行,而是返回一个生成器对象:
python
g = my_gen()
print(g) # <generator object my_gen at ...>
2. 普通函数 vs 生成器函数
特性 | 普通函数 | 生成器函数 |
---|---|---|
使用关键字 | return |
yield |
返回什么 | 一次性返回所有结果 | 每次返回一个值 |
内存消耗 | 可能较大 | 非常省内存 |
可暂停执行 | ❌ 不可以 | ✅ 可以暂停并恢复执行 |
3. yield
是啥?怎么用?
yield
是生成器函数的灵魂。它会把函数暂停,把值"送"出去,并等待下一次调用。
python
def count_up_to(max):
count = 1
while count <= max:
yield count
count += 1
用法:
python
for num in count_up_to(3):
print(num)
输出:
python
1
2
3
每次 yield
一个值,下一轮从 yield
的下一行接着运行。
4. 用 for
遍历生成器函数
这是最常见的用法。其实背后是迭代器的原理(用 next()
一点点去取),但你不用管这些,只管像遍历列表一样用就行:
python
def gen_words():
yield "hello"
yield "world"
for word in gen_words():
print(word)
输出:
python
hello
world
5. yield
与 return
的区别
对比点 | yield |
return |
---|---|---|
暂停函数 | ✅ 是 | ❌ 否(直接结束函数) |
返回值 | 一次一个 | 一次性全部 |
多次调用 | 每次从上次 yield 后继续 |
每次都重新开始 |
例子对比:
python
def func_return():
return 1
return 2 # 永远不会执行
def func_yield():
yield 1
yield 2
6. 用 next()
手动调用生成器
可以不用 for
,直接用 next()
来一个一个取值:
python
g = (x * 2 for x in range(3))
print(next(g)) # 0
print(next(g)) # 2
print(next(g)) # 4
# print(next(g)) # StopIteration
对生成器函数也是一样:
python
def simple_gen():
yield "A"
yield "B"
g = simple_gen()
print(next(g)) # A
print(next(g)) # B
7. yield from
:更优雅地嵌套生成器
如果你要在一个生成器中调用另一个生成器,可以用 yield from
代替一大堆 for
循环。
传统写法:
python
def outer():
for val in [1, 2, 3]:
yield val
更简洁写法:
python
def outer():
yield from [1, 2, 3]
也支持嵌套生成器函数:
python
def gen1():
yield "A"
yield "B"
def gen2():
yield from gen1()
yield "C"
for x in gen2():
print(x)
输出:
python
A
B
C
8. 生成器函数的应用场景
生成器函数在处理大量数据 或流式数据时非常好用:
📄 场景1:按行读取大文件
python
def read_large_file(path):
with open(path, "r") as f:
for line in f:
yield line.strip()
🔢 场景2:无限序列(如斐波那契)
python
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
scss
f = fib()
for _ in range(10):
print(next(f))
🎲 场景3:模拟数据流或日志流
python
import time
def log_stream():
count = 0
while True:
yield f"log line {count}"
count += 1
time.sleep(1)
9. 注意事项与陷阱
- 🚫 生成器只能遍历一次,想再用得重新生成
- ❗不能随便混用
yield
和return
,尤其是在同一个代码路径上 - ⛔
yield
后的函数不会立马执行,只有取值时才会跑 - 📉 如果里面抛异常没处理,会导致整个生成器终止
10. 小结和写法建议
🔑 关键词记牢:
yield
产生一个值,函数暂停next()
继续执行生成器yield from
简化嵌套- 生成器 = 惰性计算 + 节省内存 + 逻辑清晰
✅ 什么时候用生成器函数?
- 数据太大不能一次加载
- 想要按需生成值(比如实时日志、数据流)
- 想写更优雅的代码逻辑,减少临时变量