生成器
>>> def make_counter(x):
... print('entering make_counter')
... while True:
... yield x ①
... print('incrementing x')
... x = x + 1
...
>>> counter = make_counter(2) ②
>>> counter ③
<generator object at 0x001C9C10>
>>> next(counter) ④
entering make_counter
2
>>> next(counter) ⑤
incrementing x
3
>>> next(counter) ⑥
incrementing x
4
① | make_counter 中出现的 yield 命令的意思是这不是一个普通的函数。它是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的 生成器【Generator】。 |
② | 为创建 make_counter 生成器的实例,仅需像调用其它函数那样对它进行调用。注意该调用并不实际执行函数代码。可以这么说,是因为 make_counter() 函数的第一行调用了 print() ,但实际并未打印任何内容。 |
③ | 该 make_counter() 函数返回了一个生成器对象。 |
④ | next() 函数以一个生成器对象为参数,并返回其下一个值。对 counter 生成器第一次调用 next() ,它针对第一条 yield 语句执行 make_counter() 中的代码,然后返回所产生的值。在此情况下,该代码输出将为 2 ,因其仅通过调用 make_counter(2) 对生成器进行初始创建。 |
⑤ | 对同一生成器对象反复调用 next() 将确切地从上次调用的位置开始继续,直到下一条 yield 语句。所有的变量、局部数据等内容在 yield 时被保存,在 next() 时被恢复。下一行代码等待被执行以调用 print() 以打印出 incrementing x 。之后,执行语句 x = x + 1 。然后它继续通过 while 再次循环,而它再次遇上的第一条语句是 yield x ,该语句将保存所有一切状态,并返回当前 x 的值(当前为 3 )。 |
⑥ | 第二次调用 next(counter) 时,又进行了同样的工作,但这次 x 为 4 。 |
斐波那契序列生成器
def fib(max):
a, b = 0, 1 ①
while a < max:
yield a ②
a, b = b, a + b ③
① | 斐波那契序列是一系列的数字,每个数字都是其前两个数字之和。它从 0 和 1 开始,初始时上升缓慢,但越来越快。启动该序列需要两个变量:从 0 开始的 a,和从 1 开始的 b 。 |
② | a 是当前序列中的数字,因此对它进行 yield 操作。 |
③ | b 是序列中下一个数字,因此将它赋值给 a,但同时计算下一个值 (a + b ) 并将其赋值给 b 以供稍后使用。注意该步骤是并行发生的;如果 a 为 3 且 b 为 5 ,那么 a, b = b, a + b 将会把 a 设置 5 (b 之前的值),将 b 设置为 8 ( a 和 b 之前值的和)。 |
>>> from fibonacci import fib
>>> for n in fib(1000): ①
... print(n, end=' ') ②
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> list(fib(1000)) ③
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
① | 可以在 for 循环中直接使用像 fib() 这样的生成器。for 循环将会自动调用 next() 函数,从 fib() 生成器获取数值并赋值给 for 循环索引变量。(n) |
② | 每经过一次 for 循环, n 从 fib() 的 yield 语句获取一个新值,所需做的仅仅是输出它。一旦 fib() 的数字用尽(a 大于 max,即本例中的 1000 ), for 循环将会自动退出。 |
③ | 这是一个很有用的用法:将一个生成器传递给 list() 函数,它将遍历整个生成器(就像前例中的 for 循环)并返回所有数值的列表。 |