小编典典
注意:本文采用Python 3.x语法。†
一个发电机仅仅是它返回一个对象,你可以调用一个函数next,这样在每次调用它返回一定的价值,直到它提出了一个StopIteration例外,这表明所有值已经产生。这样的对象称为迭代器。
普通函数使用来返回单个值return,就像Java中一样。但是,在Python中,有一个替代方法称为yield。yield在函数中的任何地方使用它都会使其生成器。遵守以下代码:
>>> def myGen(n):
... yield n
... yield n + 1
...
>>> g = myGen(6)
>>> next(g)
6
>>> next(g)
7
>>> next(g)
Traceback (most recent call last):
File "", line 1, in
StopIteration
如你所见,myGen(n)是一个产生n和的函数n + 1。每次调用都会next产生一个值,直到产生所有值为止。for循环next在后台调用,因此:
>>> for n in myGen(6):
... print(n)
...
6
7
同样,还有生成器表达式,它们提供了一种方法来简要描述某些常见的生成器类型:
>>> g = (n for n in range(3, 5))
>>> next(g)
3
>>> next(g)
4
>>> next(g)
Traceback (most recent call last):
File "", line 1, in
StopIteration
请注意,生成器表达式非常类似于列表推导:
>>> lc = [n for n in range(3, 5)]
>>> lc
[3, 4]
观察到生成器对象仅生成一次,但是它的代码不会一次全部运行。仅调用以next实际执行(部分)代码。一旦yield到达语句,生成器中的代码将停止执行,并在该语句上返回值。next然后,对的下一个调用使执行在生成器在最后一个生成器之后被保留的状态下继续执行yield。这是常规函数的根本区别:常规函数始终在“顶部”开始执行,并在返回值时丢弃其状态。
关于这个主题还有更多的事情要说。例如,可以将send数据返回到生成器(参考)中。但这是我建议你在了解发电机的基本概念之前不要研究的东西。
现在你可能会问:为什么使用发电机?有两个很好的理由:
使用生成器可以更简洁地描述某些概念。
无需创建返回值列表的函数,而是可以编写一个生成器以动态生成值。这意味着不需要构造任何列表,这意味着生成的代码具有更高的内存效率。这样,甚至可以描述太大而无法容纳在内存中的数据流。
生成器允许以自然的方式描述无限的流。考虑例如斐波那契数:
>>> def fib():
... a, b = 0, 1
... while True:
... yield a
... a, b = b, a + b
...
>>> import itertools
>>> list(itertools.islice(fib(), 10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
该代码用于itertools.islice从无限流中获取有限数量的元素。建议你仔细看一下itertools模块中的功能,因为它们是轻松编写高级生成器的基本工具。
† 关于Python <= 2.6:在上面的示例中next是一个函数,该函数调用__next__给定对象上的方法。在Python <= 2.6中,使用了一种稍有不同的技术,即o.next()代替next(o)。Python 2.7具有next()call,.next因此你无需在2.7中使用以下内容:
>>> g = (n for n in range(3, 5))
>>> g.next()
3
2020-02-08