生成器
1. 什么是生成器
- 生成器(generator):一边循环一边计算的机制,不一次性创建完整 list,按需算出下一个元素,节省内存。
- 列表生成式会一次性生成整个 list;若元素可按算法推算,用生成器可在循环中边算边取,不必占用大量空间。
2. 创建方式一:列表生成式的 [] 改成 ()
- 把列表生成式的
[]改成(),得到的就是 generator,不是 list。
L = [x * x for x in range(10)] # list:[0, 1, 4, ..., 81]
g = (x * x for x in range(10)) # generator 对象
- L 和 g 的区别:仅在于最外层的
[]和();L 是 list,g 是 generator。
3. 获取 generator 的元素
next(g)
next(g):计算出 generator 的下一个元素并返回;没有更多元素时抛出StopIteration。- generator 保存的是算法,每次
next(g)才算出下一个值。
g = (x * x for x in range(10))
next(g) # 0
next(g) # 1
# ... 直到 next(g) 抛出 StopIteration
for 循环(推荐)
- generator 是可迭代对象,用
for循环迭代即可,不必手动next(),也不必处理StopIteration。
g = (x * x for x in range(10))
for n in g:
print(n) # 0, 1, 4, 9, ..., 81
记忆:创建 generator 后,基本用 for 迭代,很少直接调 next()。
4. 创建方式二:函数 + yield
- 若推算逻辑复杂,列表生成式写不出来,可用函数实现;在函数里用
yield代替return,函数就变成 generator 函数。 - 调用 generator 函数:返回一个 generator 对象,不会立刻执行完函数体;每次
next()或 for 取下一个值时,才执行到下一个yield并返回。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 遇到 yield 就暂停并返回值
a, b = b, a + b
n = n + 1
return 'done'
f = fib(6) # f 是 generator 对象,不是 'done'
for n in f:
print(n) # 1, 1, 2, 3, 5, 8
a, b = b, a + b:相当于t = (b, a+b); a, b = t[0], t[1],不必写临时变量。
5. generator 函数的执行流程
- 普通函数:顺序执行,遇到
return或执行完最后一行就返回。 - generator 函数:每次调用
next()时执行,遇到yield就中断并返回值;再次next()时从上次 yield 之后继续执行,直到下一个 yield 或函数结束。
def odd():
print('step 1')
yield 1
print('step 2')
yield 3
print('step 3')
yield 5
o = odd()
next(o) # 打印 step 1,返回 1
next(o) # 打印 step 2,返回 3
next(o) # 打印 step 3,返回 5
next(o) # StopIteration
注意:多次调用 generator 函数会创建多个独立的 generator。next(odd()) 每次都会新建 generator,所以每次都只执行到第一个 yield(返回 1);正确写法是 g = odd(),然后对同一个 g 多次 next(g)。
6. for 循环与 return 返回值
- 用 for 迭代 generator 时,拿不到函数里
return的返回值(for 在遇到 StopIteration 时结束,不会把 return 值给你)。 - 若要拿到 return 的值,必须捕获 StopIteration,返回值在
e.value中。
g = fib(6)
while True:
try:
x = next(g)
print('g:', x)
except StopIteration as e:
print('Generator return value:', e.value) # 'done'
break
小结
| 要点 | 说明 |
|---|---|
| 生成器 | 边循环边计算,不一次性建完整 list,省内存 |
| 创建方式一 | 列表生成式 [] 改成 () → generator |
| 创建方式二 | 函数里用 yield → generator 函数,调用返回 generator 对象 |
| 取元素 | next(g) 或 for n in g;推荐 for |
| 执行流程 | 遇到 yield 暂停返回,下次从 yield 后继续;return 或执行完即结束 |
| 普通函数 vs generator 函数 | 普通函数调用直接返回结果;generator 函数调用返回 generator 对象 |
- 原理:在 for 循环中不断算出下一个元素,在适当条件结束;函数型 generator 遇到 return 或执行到最后一行即结束。
- 区分:
r = abs(6)得到结果;g = fib(6)得到 generator 对象,迭代 g 才得到值。
练习:杨辉三角——写一个 generator 函数,不断 yield 下一行的 list(如 [1], [1,1], [1,2,1], ...)。