Python高级编程之迭代器与生成器

1.迭代器

迭代器只不过是一个实现迭代器协议的容器对象,它基于两个方法:

  • next返回容器的下一个项目
  • __iter__返回迭代器本身
迭代器可以通过使用一个iter内建函数和一个序列来创建:
In [1]: i = iter('abcd')

In [2]: i.next()
Out[2]: 'a'

In [3]: i.next()
Out[3]: 'b'

In [4]: i.next()
Out[4]: 'c'

In [5]: i.next()
Out[5]: 'd'

In [6]: i.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
/home/ubuntu/<ipython-input-6-e590fe0d22f8> in <module>()
----> 1 i.next()

StopIteration: 
 
 当序列遍历完时,会抛出一个StopIteration异常。这将使迭代器与循环兼容,因为他们将捕获这个异常以停止循环。要创建定制的迭代器,可以编写一个具有next方法的类,只要该类能够提供返回迭代器实例的__iter__特殊方法。 
 
class MyIterator(object):
    def __init__(self,step):
        self.step = step
    def next(self):
        """Returns the next element."""
        if self.step == 0:
            raise StopIteration
        self.step -= 1
        return self.step
    def __iter__(self):
        """Returns the iterator itself."""
        return self
 
for el in MyIterator(4):
    print el
执行结果当然为3,2,1,0.迭代器本身是一个底层的特性和概念,在程序中可以没有它们,但是它们为生成器一更有趣的特定提供了基础。

2. 生成器


yield

从Python2.2起,生成器提供了一种出色的方法,使得需要返回一系列元素的函数所需的代码更加简洁高效。基于yield指令,可以暂停一个函数并返回中间结果。该函数将保存执行韩静并且可以在必要时回复。
例如:Fibonacci数列可以用一个迭代器来实现,如下:
def fibonacci():
    a,b = 0, 1
    while True:
        yield b
        a, b = b, a+b
    
fib = fibonacci()
print fib.next()
print fib.next()
print fib.next()
print [fib.next() for i in range(10)]
结果为:
1
1
2
[3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
该函数返回一个特殊的迭代器,也就是generator对象,它知道如何保存执行环境。对它的调用是不确定的,每次都将产生序列中的下一个元素。这种语法很简洁,算法的不确定特定并没有影响代码的可读性。不必提供使函数停止的方法。实际上,这看上去就像用伪代码设计程序序列一样。
在开发中,生成器并不那么常用,因为开发人员还不习惯这种思考。开发人员多年来习惯于使用意图明确的函数。当需要一个将返回一个序列或在循环中执行的函数时,就应该考虑生成器。当这些元素将被传递到另一个函数中以进行后续的处理时,一次返回一个元素能提高整体的性能。
在这种情况下,用于处理一个元素的资源通常不如用于整个过程的资源重。因此,它们可以仍然保持位于底层,使程序更加高效。例如,Fibonacci数列是无穷尽的,但是用来生成它的生成器不需要在提供一个值得时候,就预先占用无穷多的内存。常见的应用场景是使用生成器的数据流缓冲区。使用这些数据的第三方程序代码可以暂停、回复和停止生成器,所有数据在开始这一过程之前不需要全部导入。
生成器对降低程序复杂性也有帮助,并且能够提升基于多个序列的数据转换算法的性能。把每个序列当做一个迭代器,然后将他们合并到一个高级的函数中,这是一种避免函数变得庞大、丑陋、不可理解的好办法。而且,这可以给整个处理链提供实时的反馈。
在下面的实例中,每个函数用来在序列上定义一个转换,然后它们被链接起来应用,每次调用将处理一个元素并返回结果,如下:
def power(values):
    for value in values:
        print 'powering %s' % value
        yield value

def adder(values):
    for value in values:
        print 'adding to %s' % value
        if value % 2 == 0:
            yield value + 3
        else:
            yield value + 2

elements = [1, 4, 7, 9, 12, 19]
res = adder(power(elements))
print res.next()
print res.next()
print res.next()
注意:保持代码的简单,而不是数据,拥有许多简单的处理序列的可迭代函数,要比一个复杂的、每次计算一个值得函数更好一些。

send

Python引入的与生成器相关的最后一个特定提供了与next方法调用的代码进行交互的功能。yield将变成一个表达式,而 一个值可以通过名为send的方法来传递,如下:
def psychologist():
    print 'Please tell me your problems'
    while True:
        answer = (yield)
        if answer is not None:
            if answer.endswith('?'):
                print ("Don't ask yourself too much questions")
            elif 'good' in answer:
                print "A that's good, go on"
            elif 'bad' in answer:
                print "Don't be so negative"

free = psychologist()
free.next()
free.send('I feel bad')
free.send("Why I shouldn't?")
free.send('OK then i shoud find what is good for me ')
执行结果为:
Please tell me your problems
Don't be so negative
Don't ask yourself too much questions
A that's good, go on

send的工作机制与next一样,但是yield将变成能够返回传入的值。因而,这个函数可以根据客户端代码来改变行为。同时,还添加了throw和close两个函数以完成该行为。它们将向生成器抛出一个错误:
  • throw允许客户端代码传入一个抛出的任何类型的异常
  • close的工作方式是相同的,但是将抛出一个特定的异常----GeneratorExit,在这种情况下,生成器函数必须再次抛出GeneratorExit或StopIteration异常。
def my_generator():
    while True:
        try:
            yield 'something'
        except ValueError:
            yield 'dealing with the exception'
        finally:
            print "ok, let's clean"
In [2]: gen = my_generator()

In [3]: gen.next()
Out[3]: 'something'

In [4]: gen.next()
ok, let's clean
Out[4]: 'something'

In [5]: gen.throw(ValueError('mean mean mean'))
Out[5]: 'dealing with the exception'

In [6]: gen.close()
ok, let's clean

In [7]: gen.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
/tmp/<ipython-input-7-b2c61ce5e131> in <module>()
----> 1 gen.next()

StopIteration: 
finally部分将捕获任何未被捕获的close和throw调用,是完成清理工作的推荐方式。GeneratorExit异常在生成器中无法捕获,因为它被编译器用来调用close时确定是否正常退出。如果有代码与这个异常关联,那么结实程序将抛出一个系统错误并退出。
有了这几个方法,就可以使用生成器来编写协同程序(coroutine)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值