目录python
说迭代器以前有个相关的名词须要介绍:
可迭代对象:只要定义了__iter__()方法,咱们就说该对象是可迭代对象,而且可迭代对象能提供迭代器。并发
在Python中,for循环能够用于Python中的任何类型,包括列表、元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器。app
迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有__next__方法的对象会前进到下一结果,而到一系列结果的末尾,则会引起StopIteration。任何这类的对象在Python中均可以用for循环或其余遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,而且捕捉StopIteration异常来肯定什么时候离开。函数
使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会形成内存的过大开销。工具
好比要逐行读取一个文件的内容,利用readlines()方法,咱们能够这么写:线程
for line in open("test.txt").readlines(): print line
这样虽然能够工做,但不是最好的方法。由于他其实是把文件一次加载到内存中,而后逐行打印。当文件很大时,这个方法的内存开销就很大了。code
利用file的迭代器,咱们能够这样写:对象
for line in open("test.txt"): #use file iterators print line
这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。接口
使用内建的工厂函数iter(iterable)能够获取迭代器对象(对象包含__iter__方法便可迭代,__iter__方法返回一个迭代器):内存
>>> lst = range(5) >>> it = iter(lst) >>> it <listiterator object at 0x0000000001E43390>
使用next()方法访问下一个元素
>>> it.next() 0 >>> it.next() 1 >>> it.next() 2
python处理迭代器越界是抛出StopIteration异常
>>> it.next() 3 >>> it.next <method-wrapper 'next' of listiterator object at 0x01A63110> >>> it.next() 4 >>> it.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
了解了StopIteration,可使用迭代器进行遍历了:
lst = range(5) it = iter(lst) try: while True: val = it.next() print val except StopIteration: pass
for语法糖:
>>> lst = range(5) >>> for i in lst: ... print i ... 0 1 2 3 4
生成器函数在Python中与迭代器协议的概念联系在一块儿。简而言之,包含yield语句的函数会被特意编译成生成器。当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口。函数也许会有个return语句,但它的做用是用来yield产生值的。
Python使用生成器对延迟操做提供了支持。所谓的延迟操做,是指在须要的时候才产生结果,而不是当即产生结果。因此生成器也有了以下的好处:
使用斐波那契数列的例子来讲明一下:
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n+ 1 #执行 for n in fab(5): print n 1 1 2 3 5
该函数经过yield关键字来返回结果,每一次迭代就中止于yield语句处,一直到下一次迭代。
生成器也是一种迭代器,简单地讲,yield 的做用就是把一个函数变成一个 generator,带有 yield 的函数再也不是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是彻底同样的,因而函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程当中被 yield 中断了数次,每次中断都会经过 yield 返回当前的迭代值。
也能够手动调用 fab(5) 的 next() 方法(由于 fab(5) 是一个 generator 对象,该对象具备 next() 方法),这样咱们就能够更清楚地看到 fab 的执行流程:
>>> f = fab(3) >>> f.next() 1 >>> f.next() 1 >>> f.next() 2 >>> f.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
在一个生成器中,若是没有return,则默认执行到函数完毕;若是遇到return,若是在执行过程当中 return,则直接抛出 StopIteration 终止迭代。例如:
def read_file(fpath): BLOCK_SIZE = 1024 with open(fpath, 'rb') as f: while True: block = f.read(BLOCK_SIZE) if block: yield block else: return
若是直接对文件对象调用 read() 方法,会致使不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。经过 yield,咱们再也不须要编写读文件的迭代类,就能够轻松实现文件读取。