Python 高级编程之初识生成器

在阅读这篇文章以前,你应该已经了解Python中的迭代器。如果不清楚的话,可以参考我的另一篇文章:《Python高级编程之初识迭代器》

1. yield语句

在之前的文章中,我们使用类来创建自己的迭代器,实现过程稍微麻烦一点:

class yrange:
    def __init__(self, start=0, stop=0):
        self.start = start
        self.stop = stop
    def __iter__(self):
        return self
    def next(self):
        if(self.start < self.stop):
            start = self.start
            self.start += 1
            return start
        else:
            raise StopIteration

生成器函数可以简化迭代器的创建。只要在函数中使用 yield 语句来替代结果的输出,就变成了一个生成器函数。

下面的代码实现了与上述的类等价的功能

def zrange(start=0, stop=0):
    while start < stop:
        yield start
        start += 1

可以看到,实现的过程非常简洁。

2. 生成器

生成器的工作方式与迭代器相同,可以被视为是迭代器的一种。需要注意的是,生成器只支持一遍的迭代。当一个生成器被迭代完之后,就无法再产生结果,此时必须使用一个新的生成器以便再次迭代。

如果我们调用上面示例中定义的生成器函数,将会得到一个生成器。

>>> zrange(0, 10)
<generator object zrange at 0x03910BC0>

我们可以像使用其它迭代器一样使用生成器。

>>> for z in zrange(5, 8):
...    print z
5
6
7

3. 生成器函数工作原理

生成器函数与普通函数最显著的不同就是它没有 return 语句,取而代之的是 yield 语句。

正常的函数在被调用后,会通过 return 语句返回一个值,然后结束。而生成器函数被调用后,返回的生成器类似于一个函数环境,每次迭代过程中执行到 yield 语句,就会返回一个值,然后挂起函数,等下一次迭代时再恢复函数的状态,继续执行其它语句,直到再次遇到 yield 语句时重复上述过程。

我们用一个例子来观察生成器函数的运行过程:

>>> def gen(stop):
...     start = 0
...     print "Generator starts"
...     while(start < stop):
...         print "Before", start
...         yield start
...         print "After", start
...         start += 1
...     print "Generator stops"
>>> G = gen(3)
>>> G.next()
Generator starts
Before 0
0
>>> G.next()
After 0
Before 1
1
>>> G.next()
After 1
Before 2
2
>>> G.next()
After 2
Generator stops
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
StopIteration

可以看到,每次执行到 yield 语句,函数将被挂起,在下次迭代时函数恢复,继续执行。

在迭代到达末尾时,生成器触发了 StopIteration 异常,遵循迭代协议。单从生成器的工作方式来看,与迭代器是一样的。

4. 生成器表达式

除了通过生成器函数来得到生成器之外,还可以使用生成器表达式来实现。

生成器表达式与列表推导式的语法一致,不同之处在于生成器表达式使用一对圆括号来包围表达式,而列表推导式使用的是方括号。

生成器表达式的基本用法如下:

>>> L = [1, 2, 3]
>>> G = (x ** 2 for x in L)

变量 G 就是我们得到的生成器,下面对它进行迭代

>>> for i in G:
...     print i
1
4
9

这里只提到了生成器表达式最简单的语法,如果要更深入了解生成器表达式,可以参考具有相同语法的列表推导式,只要把表达式的方括号替换为圆括号即可。

详细介绍请参考:《Python高级编程之列表推导式》

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注