[python]--迭代器,生成器补充
在python中,list,string,dict都是可迭代对象,可以通过for语句遍历.
迭代器
迭代器对象要求支持迭代器协议的对象,在python中,支持迭代器协议就算实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身;
next()方法返回容器的下一个元素,在结尾时引发StopIteration异常
__iter__()和next()方法
这两个方法是迭代器最基本的方法,一个用来获得迭代器对象,一个用来获取容器中的下一个元素.
对于可迭代对象,可以使用内建函数iter()来获取它的迭代器对象:
iter()方法可以获得list的迭代器对象,然后就可以使用__next__()方法来访问list中的元素了.当容器中没有可访问的元素后,__next__()方法就会抛出一个
StopIteration异常终止迭代器.
不使用iter()方法的话,可以使用next()方法对可迭代对象,来获取下一个元素
自定义迭代器
了解了迭代器协议之后,就可以自定义迭代器了.
下面例子中实现了一个MyRange的类型,这个类型中实现了__iter__()方法,通过这个方法返回对象本身作为迭代器对象;同时,实现了next()方法用来获取容器中的下一个元素,当没有可访问元素后,就抛出StopIteration异常.
class MyRange(object): def __init__(self, n): self.idx = 0 self.n = n def __iter__(self): return self def next(self): if self.idx < self.n: val = self.idx self.idx += 1 return val else: raise StopIteration()
这个自定义类型跟内建函数xrange很类似,看一下运行结果:
生成器
在python中,使用生成器可以很方便的支持迭代器协议.生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议.
也就是说,yield是一个语法糖.内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态.
下面看看生成器的使用:
在这个例子中,定义了一个生成器函数,函数返回一个生成器对象,然后就可以通过for语句进行迭代访问.
其实,生成器函数返回生成器的迭代器,"生成器的迭代器"这个术语通常被称作"生成器".要注意的是生成器就算一类特殊的迭代器.作为一个迭代器,生成器必须要定义一些方法,其中一个就是next().如同迭代器一样,我们可以使用next()函数获取下一个值.
生成器执行流程
- 当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有执行.
- 当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止
- next()方法的返回值就是yield语句处的参数(yielded value)
- 当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常
生成器表达式
在开始介绍生成器表达式之前,先看看我们比较熟悉的列表解析(List comprehensions),列表解析一般都是下面的形式.
[expr for iter_var in iterable if cond_expr]
迭代iterable里所有内容,每一次迭代后,把iterable里满足cond_expr条件的内容放到iter_var中,再在表达式expr中应该iter_var的内容,最后用表达式的计算值生存一个列表.
例如,生成一个list来保存50以内的所有奇数:
[i for i in range(50) if i%2]
生成器表达式是在python2.4中引入的,当序列过长,而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析.生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[],如下:
(expr for iter_var in iterable if cond_expr)
生成器表达式并不是创建一个列表,而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目"产生"(yield)出来,生成器表达式使用惰性计算,只有在检索时才被赋值,所以在列表比较长的情况下使用内存上更有效
生成器表达式产生的生成器,自身是一个可迭代对象,同时也是迭代器本身
递归生成器
生成器可以向函数一样进行递归使用,下面看一个简单的例子,对一个序列进行全排列:
def permutations(li): if len(li) == 0: yield li else: for i in range(len(li)): li[0], li[i] = li[i], li[0] for item in permutations(li[1:]): yield [li[0]] + item for item in permutations(range(3)): print item
生成器的send()和close()方法
生成器中还有两个很重要的方法:send()和close()
send(value):
从前面了解到,next()方法可以恢复生成器状态并继续执行,其实send()是除next()外另一个恢复生成器的方法.
python2.5中,yield语句变成了yield表达式,也就是说yield可以有一个值,而这个值就是send()方法的参数,所以send(None)和next()是等效的.同样,next()和send()的返回值都是yield语句处的参数
关于send():调用send传入非None值前,生成器必须处于挂起状态,否则将抛出异常
close():
这个方法用来关闭生成器,对关闭的生成器后再调用next或send将抛出异常