迭代器和生成器是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 <type 'exceptions.StopIteration'>
迭代器实际是一种底层的特性和概念,但是为生成器提供了一种支持。
生成器
有记忆性的函数暂停和恢复执行的表现,使用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]: <generator object <genexpr> 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) <ipython-input-6-fb651abe6a2e> in <module>() ----> 1 gener.next() StopIteration:
关于生成器,还有两点需要说明一下,第一,yield(和return)关键字只能存在于函数中,否则会报类似语法错误:SyntaxError: 'return' outside function;第二,生成器不一定必须使用for语句,也可以线性使用多个yield语句(上面的例子只是为了少写代码而已)
迭代器和生成器的关系
我关于这个问题,纠结了好久,不过还没够能力或时间读源码。还是简单写下我的理解。
我觉得迭代器和生成器可以认为是无关系,迭代器更为底层,更为不常见,如果硬贴上层关系,那就是他们的next方法和StopIterator异常是相同或相似的实现,也或者是共有的同一种实现(这句话的意思是生成器是在迭代器的基础上实现的)。
这还只是python冰山一角,我就不浪费时间拘泥他们到底是什么关系了,爱谁谁,我喜欢就可以。
关于这一主题,欢迎大家留言探讨。