我正在阅读Python烹饪书,目前正在研究生成器。我觉得很难理解。
由于我有Java背景,那么Java中是否有对等的语言?这本书讲的是“生产者/消费者”,但当我听到这个词时,我想到了线程。
什么是发电机,你为什么要用它?显然,没有引用任何书籍(除非你能直接从一本书中找到一个体面、简单的答案)。如果你慷慨的话,还可以举个例子!
我正在阅读Python烹饪书,目前正在研究生成器。我觉得很难理解。
由于我有Java背景,那么Java中是否有对等的语言?这本书讲的是“生产者/消费者”,但当我听到这个词时,我想到了线程。
什么是发电机,你为什么要用它?显然,没有引用任何书籍(除非你能直接从一本书中找到一个体面、简单的答案)。如果你慷慨的话,还可以举个例子!
当前回答
我给出了这段代码,解释了关于生成器的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
...
我给出了这段代码,解释了关于生成器的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)
注意:本文假设Python 3。x语法。__
生成器只是一个函数,它返回一个对象,接下来可以对其调用,这样对于每次调用它都会返回一些值,直到引发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]
Observe that a generator object is generated once, but its code is not run all at once. Only calls to next actually execute (part of) the code. Execution of the code in a generator stops once a yield statement has been reached, upon which it returns a value. The next call to next then causes execution to continue in the state in which the generator was left after the last yield. This is a fundamental difference with regular functions: those always start execution at the "top" and discard their state upon returning a value.
关于这个问题还有很多要说的。例如,可以将数据发送回生成器(参考)。但是,我建议您在了解生成器的基本概念之前不要研究这个问题。
现在你可能会问:为什么要使用生成器?有几个很好的理由:
Certain concepts can be described much more succinctly using generators. Instead of creating a function which returns a list of values, one can write a generator which generates the values on the fly. This means that no list needs to be constructed, meaning that the resulting code is more memory efficient. In this way one can even describe data streams which would simply be too large to fit in memory. Generators allow for a natural way to describe infinite streams. Consider for example the Fibonacci numbers: >>> 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] This code uses itertools.islice to take a finite number of elements from an infinite stream. You are advised to have a good look at the functions in the itertools module, as they are essential tools for writing advanced generators with great ease.
†关于Python <=2.6:在上面的例子中,next是一个函数,它调用给定对象的__next__方法。在Python <=2.6中使用了稍微不同的技术,即o.next()而不是next(o)。Python 2.7有next()调用.next,所以你不需要在2.7中使用下面的方法:
>>> g = (n for n in range(3, 5))
>>> g.next()
3
对于Stephan202的回答,我唯一能补充的是建议您看一看David Beazley的PyCon '08演示文稿“生成器技巧给系统程序员”,这是我所见过的关于如何以及为什么使用生成器的最好的解释。这就是让我从“Python看起来很有趣”变成“这就是我一直在寻找的东西”的原因。网址是http://www.dabeaz.com/generators/。
首先,术语生成器最初在Python中定义不清,导致了很多混乱。你可能指的是迭代器和可迭代对象(参见这里)。然后在Python中还有生成器函数(返回生成器对象)、生成器对象(迭代器)和生成器表达式(求值为生成器对象)。
根据generator的术语表条目,现在的官方术语似乎是generator是“generator function”的缩写。在过去,文档对术语的定义不一致,但幸运的是,这个问题已经得到了解决。
在没有进一步说明的情况下,精确地避免使用术语“生成器”可能仍然是一个好主意。