小编典典

了解 Python 中的生成器

all

我目前正在阅读 Python 食谱,目前正在研究生成器。我发现我的头很难转。

由于我来自 Java 背景,是否有 Java 等价物?这本书谈论的是“生产者/消费者”,但是当我听到我想到线程时。

什么是生成器,为什么要使用它?显然,无需引用任何书籍(除非您可以直接从书中找到一个体面、简单的答案)。如果你觉得慷慨的话,也许有例子!


阅读 96

收藏
2022-05-30

共1个答案

小编典典

注意:这篇文章假定使用 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 "<stdin>", line 1, in <module>
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 "<stdin>", line 1, in <module>
StopIteration

请注意,生成器表达式很像
列表推导

>>> lc = [n for n in range(3, 5)]
>>> lc
[3, 4]

观察生成器对象是 一次 生成的,但它的代码 不是
一次性运行的。仅调用next实际执行(部分)代码。yield一旦到达语句,生成器中代码的执行就会停止,并返回一个值。下一次调用nextthen
会导致执行在最后一次之后离开生成器的状态下继续执行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:*在 上面的例子中是一个函数,它调用给定对象的方法。在 Python <=2.6 中,使用一种稍微不同的技术,即代替.
Python 2.7 具有调用功能,因此您无需在 2.7 中使用以下内容:
*next``__next__``o.next()``next(o)``next()``.next

>>> g = (n for n in range(3, 5))
>>> g.next()
3
2022-05-30