我正在阅读Python烹饪书,目前正在研究生成器。我觉得很难理解。

由于我有Java背景,那么Java中是否有对等的语言?这本书讲的是“生产者/消费者”,但当我听到这个词时,我想到了线程。

什么是发电机,你为什么要用它?显然,没有引用任何书籍(除非你能直接从一本书中找到一个体面、简单的答案)。如果你慷慨的话,还可以举个例子!


当前回答

生成器实际上是一个函数,它在完成之前返回(数据),但它在该点暂停,您可以在该点恢复该函数。

>>> def myGenerator():
...     yield 'These'
...     yield 'words'
...     yield 'come'
...     yield 'one'
...     yield 'at'
...     yield 'a'
...     yield 'time'

>>> myGeneratorInstance = myGenerator()
>>> next(myGeneratorInstance)
These
>>> next(myGeneratorInstance)
words

等等。生成器的(或一个)好处是,因为它们一次处理一块数据,所以您可以处理大量数据;对于列表,过多的内存需求可能成为一个问题。生成器,就像列表一样,是可迭代的,所以它们可以以相同的方式使用:

>>> for word in myGeneratorInstance:
...     print word
These
words
come
one
at 
a 
time

例如,请注意生成器提供了另一种处理无穷大的方法

>>> from time import gmtime, strftime
>>> def myGen():
...     while True:
...         yield strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())    
>>> myGeneratorInstance = myGen()
>>> next(myGeneratorInstance)
Thu, 28 Jun 2001 14:17:15 +0000
>>> next(myGeneratorInstance)
Thu, 28 Jun 2001 14:18:02 +0000   

生成器封装了一个无限循环,但这不是问题,因为每次您都只能得到每个答案。

其他回答

它有助于明确区分函数foo和生成器foo(n):

def foo(n):
    yield n
    yield n+1

Foo是一个函数。 Foo(6)是一个生成器对象。

使用生成器对象的典型方式是在循环中:

for n in foo(6):
    print(n)

循环打印

# 6
# 7

可以将生成器视为可恢复函数。

Yield的行为类似于return,产生的值被生成器“返回”。然而,与return不同的是,下一次生成器被请求一个值时,生成器的函数foo将从它停止的地方恢复——在最后一个yield语句之后——并继续运行,直到遇到另一个yield语句。

在幕后,当您调用bar=foo(6)时,生成器对象bar为您定义了一个下一个属性。

你可以自己调用它来获取foo产生的值:

next(bar)    # Works in Python 2.6 or Python 3.x
bar.next()   # Works in Python 2.5+, but is deprecated. Use next() if possible.

当foo结束时(并且没有更多的输出值),调用next(bar)将抛出StopInteration错误。

生成器实际上是一个函数,它在完成之前返回(数据),但它在该点暂停,您可以在该点恢复该函数。

>>> def myGenerator():
...     yield 'These'
...     yield 'words'
...     yield 'come'
...     yield 'one'
...     yield 'at'
...     yield 'a'
...     yield 'time'

>>> myGeneratorInstance = myGenerator()
>>> next(myGeneratorInstance)
These
>>> next(myGeneratorInstance)
words

等等。生成器的(或一个)好处是,因为它们一次处理一块数据,所以您可以处理大量数据;对于列表,过多的内存需求可能成为一个问题。生成器,就像列表一样,是可迭代的,所以它们可以以相同的方式使用:

>>> for word in myGeneratorInstance:
...     print word
These
words
come
one
at 
a 
time

例如,请注意生成器提供了另一种处理无穷大的方法

>>> from time import gmtime, strftime
>>> def myGen():
...     while True:
...         yield strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())    
>>> myGeneratorInstance = myGen()
>>> next(myGeneratorInstance)
Thu, 28 Jun 2001 14:17:15 +0000
>>> next(myGeneratorInstance)
Thu, 28 Jun 2001 14:18:02 +0000   

生成器封装了一个无限循环,但这不是问题,因为每次您都只能得到每个答案。

我给出了这段代码,解释了关于生成器的3个关键概念:

def numbers():
    for i in range(10):
            yield i

gen = numbers() #this line only returns a generator object, it does not run the code defined inside numbers

for i in gen: #we iterate over the generator and the values are printed
    print(i)

#the generator is now empty

for i in gen: #so this for block does not print anything
    print(i)

生成器可以看作是创建迭代器的简写。它们的行为类似于Java迭代器。例子:

>>> g = (x for x in range(10))
>>> g
<generator object <genexpr> at 0x7fac1c1e6aa0>
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> list(g)   # force iterating the rest
[3, 4, 5, 6, 7, 8, 9]
>>> g.next()  # iterator is at the end; calling next again will throw
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

希望这有助于/是你正在寻找的。

更新:

正如许多其他答案所示,有不同的方法来创建生成器。你可以像上面的例子一样使用圆括号语法,也可以使用yield。另一个有趣的特性是生成器可以是“无限的”——迭代器不会停止:

>>> def infinite_gen():
...     n = 0
...     while True:
...         yield n
...         n = n + 1
... 
>>> g = infinite_gen()
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
...

对于那些具有编程语言和计算背景的人,我喜欢从堆栈框架的角度来描述生成器。

在许多语言中,有一个堆栈在其上面是当前堆栈“帧”。堆栈框架包括分配给函数局部变量的空间,包括传递给该函数的参数。

当你调用一个函数时,当前的执行点(“程序计数器”或类似的东西)被压入堆栈,一个新的堆栈帧被创建。然后执行转移到被调用函数的开始。

对于常规函数,在某个时刻函数返回一个值,堆栈就会“弹出”。函数的堆栈帧将被丢弃,并在之前的位置继续执行。

当函数是生成器时,它可以使用yield语句在不丢弃堆栈帧的情况下返回值。函数中局部变量和程序计数器的值将被保留。这允许生成器在稍后恢复,从yield语句开始继续执行,并且它可以执行更多代码并返回另一个值。

在Python 2.5之前,所有生成器都这样做。Python 2.5还增加了将值传递回生成器的功能。这样,传入的值可以作为yield语句的表达式使用,yield语句从生成器临时返回了控件(和值)。

生成器的关键优势是函数的“状态”被保留,不像常规函数,每次堆栈帧被丢弃,你就会失去所有的“状态”。第二个优点是避免了一些函数调用开销(创建和删除堆栈帧),尽管这通常是一个次要的优点。