<aside> 💡 如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
</aside>
方括号改成圆括号
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
函数定义中包含关键字 yield
,这个函数不是普通函数,而是生成器函数。调用一个生成器函数将返回生成器。
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
使用 next()
函数
next(g)
# 每次调用会计算 g 的下一个值,没有更多的元素会抛异常
使用 for
循环
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
函数实现
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
生成器函数实现
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
生成器函数每次调用 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)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
<aside>
⚠️ 调用 generator
函数会创建一个 generator
对象,多次调用 generator
函数会创建多个相互独立的 generator
。
例:
>>> next(odd())
step 1
1
>>> next(odd())
step 1
1
>>> next(odd())
step 1
1
每次调用都会返回 1,原因就在于上面的代码创建了三个独立的 generator
。正确写法如下:
>>> g = odd()
>>> next(g)
step 1
1
>>> next(g)
step 2
3
>>> next(g)
step 3
5
</aside>
>>> for n in fib(6):
... print(n)
...
1
1
2
3
5
8
generator
的返回值包含在 StopIteration
的 value
中。
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done