【Python那些事儿之八】Generators详解
by Harrison Feng in Python
生成器(Generator)在计算机科学中是用于控制循环迭代行为的一种特殊程序。简单的讲,它其实就是一个特别的
函数。在Python里,生成器是一个返回迭代器的函数。它与普通函数的区别就在于它包含一个yield语句。而yield语
句本身也只用于定义生成器。yield语句生成的是一个序列(一系列值)。这些值可以通过生成器对象的next()方法一
个一个返回。直到所有的值都返回完后,StopIteration的异常会被抛出。请看下面的代码:
-
def count_down(i):
-
print "Let's count down from %d" % i
-
while i > 0:
-
yield i
-
i -= 1
这里定义一个简单的生成器。而下面的实验则展示了生成器的特性。
-
>>> dcnt = count_down(5) # 调用生成器。
>>> dcnt
# 生成器对象。
>>> dcnt.next()
Let's count down from 5
5
>>> dcnt.next()
4
>>> dcnt.next()
3
>>> dcnt.next()
2
>>> dcnt.next()
1
>>> dcnt.next() # 所有的值都生成完后,StopIteration异常抛出。
Traceback (most recent call last):
File "", line 1, in
StopIteration
>>>
从上面的代码我们可以看出,生成器函数在被调用时并不会运行,而是当调用生气器对象的next()方法时,它
才会开始运行。而且next()返回的都是下一个值,直到所有的值都被返回完毕。其实yield语句在生成一个值后
(next()方法被调用时),它会暂停生成器函数,并记住当前的状态。当再次调用next()方法时,生成器函数被
恢复,继续生成下一个值,以此类推。上面的这个实验很好的展示了生成器的这一特性。
生成器函数提供了一种更便利的方式来创建迭代器(Iterator),而且不用关心迭代协议。生成器是一次性操作,
只能在生成器对象上完整遍历一次。如果还需要遍历,则需要重新调用生成器函数。
-
>>> dcnt = count_down(3)
>>> for i in dcnt:
... print i
...
Let's count down from 3
3
2
1
>>> for i in dcnt:
... print i
...
>>>
-
>>> dcnt = count_down(3)
>>> for i in dcnt:
... print i
...
Let's count down from 3
3
2
1
>>>
上面的实验看出,在第二次遍历生成器对象时,没有任何输出。重新创建生成器对象后则可继续遍历。
在Python里,还有一种特殊的表达式,生成器表达式(Generator Expression)。一般情况下,如果一个
表达式返回的是一个迭代器(iterator),它就是生成器表达式(Generator Expression)。
最简单的生成器表达式:
-
>>> gx = (x ** 2 for x in xrange(10))
-
>>> gx
-
<generator object <genexpr> at 0x7f0cf1f254b0>
-
>>>
从上面的代码可以看出,生成器表达式和生成器函数相似,都能创建生成器对象,从而实现遍历操作。
生成器表达式的通用语法:
-
(expression for i in s1 if cond1
-
for j in s2 if cond2
-
...
-
if condfinal)
相当于
-
for i in s1:
-
if cond1:
-
for j in s2:
-
if cond2:
-
...
-
if condfinal: yield expression
是不是感觉生成器表达式就是生成器函数的变体?其实可以这么理解。
最后,我们可以总结一下生成器的特性:
1)必须包含yield语句的函数。
2)在调用时不会运行。
3)调用生成器时生成的生成器对象只能被使用一次(遍历)。
4)返回迭代器,但是不用关心迭代协议。
by Harrison Feng in Python
阅读(5465) | 评论(0) | 转发(0) |