迭代器和生成器是python学者们经常谈到的话题,我也不能免俗,因为实在值得总结一下。

    

    迭代器

    迭代器是对可迭代对象进行操作,通过next方法一次吐出一个元素的工具。我们用到的for..in..内部使用的就是迭代器功能。

    如果要自定义一个迭代器类的话,需要满足下面的条件:

  • 需要在类中定义__iter__方法返回self自身,表示这是一个迭代器;

  • 需要定义next方法来返回迭代的值,其中应该包含StopIteration异常的判断

下面试着写一个自定义迭代器类的例子(模仿自Python高级编程一书):

class CustomIter(object):
    def __init__(self, step):
        self.step = step
            
    def __iter__(self):
        return self
    
    def next(self):
        if self.step == 0:
            return StopIteration
        
        self.step -=1
        return self.step
    
i = CustomIter(2)  #此处如果换为for i in CustomIter(2),则不需要后续的next手工调用,
                   #也不会出现StopIteration的异常,因为for循环内部实现了next调用
print i.next()
print i.next()
print i.next()

 运行结果如下:

1
0

    迭代器实际是一种底层的特性和概念,但是为生成器提供了一种支持。


    生成器

    有记忆性的函数暂停和恢复执行的表现,使用yield替代return语句,表示每次返回一个值并暂停执行。当再次被外部next调用时,利用函数上下文自动从暂停的位置继续运行,直到再次遇到yield语句。

    让我们从代码中理解上面没太听明白的话:

#!/usr/bin/env python
#encoding:utf-8

def testGenerator():  
    for x in ("first", "second", "third"):
        print "this is " + x
        yield x #每次返回x之后,会停止
        print "do other things"

b = testGenerator()
print b.next()
print u"\n------再次执行next():------\n"
#再次运行next
print b.next()

运行结果如下:

this is first
first

------再次执行next():------

do other things
this is second
second

    生成器的其他形式:

    yield表达式,即在函数体中存在var = (yield),表示可以得到客户端调用send(生成器对象的方法)发送进来的数据。以实例来进行说明:

    

#coding:utf-8

def yield_expr():
    for x in range(5):
        print "do one thing"
        a = (yield)  #暂停,准备接受send过来的数据,即触发继续运行的条件是有数据被send过来
        print a   #查看a获得的值,即是客户send发送的值
        print "do another thing"

b = yield_expr()
b.next()
print "\n-- the first call send() --:\n"
b.send(3)

print "\n-- the second call send() --:\n"
b.send(4)

运行结果如下:

do one thing
-- the first call send() --
3
do another thing
do one thing
-- the second call send() --:
4
do another thing
do one thing

运行结果说明:程序每次运行到yield表达式处会暂停,直到外部通过send发送数据进来;

    另一种生成器形式叫做生成器表达式,即类似于列表解析,只是把中括号换成了小括号,形如:

(i**2 for i in range(10) if i % 2 == 0),不同于列表解析的是,这是一个生成器,通过next()调用每次返回一个值。

    请看下面的实例代码:

In [1]: gener = (i for i in range(5))

In [2]: gener                #上面表达式得到一个生成器对象
Out[2]:  at 0x000000000369C480>

In [3]: gener.next()
Out[3]: 0

In [4]: gener.next()
Out[4]: 1

In [5]: for index in gener:  #使用for自动打印生成器内容,直到StopIteration
   ...:     print index
   ...:
2                            #从结果可以看出,生成器内部可以记录当前上下文状态,即可暂停、
3                            #可恢复
4

In [6]: gener.next()         #再次调用,触发异常
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
 in ()
----> 1 gener.next()

StopIteration:

    关于生成器,还有两点需要说明一下,第一,yield(和return)关键字只能存在于函数中,否则会报类似语法错误:SyntaxError: 'return' outside function;第二,生成器不一定必须使用for语句,也可以线性使用多个yield语句(上面的例子只是为了少写代码而已)

    

    迭代器和生成器的关系

    我关于这个问题,纠结了好久,不过还没够能力或时间读源码。还是简单写下我的理解。

    我觉得迭代器和生成器可以认为是无关系,迭代器更为底层,更为不常见,如果硬贴上层关系,那就是他们的next方法和StopIterator异常是相同或相似的实现,也或者是共有的同一种实现(这句话的意思是生成器是在迭代器的基础上实现的)。

    这还只是python冰山一角,我就不浪费时间拘泥他们到底是什么关系了,爱谁谁,我喜欢就可以。

    

    关于这一主题,欢迎大家留言探讨。