迭代器:是一个实现了迭代器协议的对象,python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration。任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开。
迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法。其中__iter__()方法返回迭代器对象本身;next()方法返回容器的下一个元素,在结尾时引发StopIteration异常。当我们使用for语句的时候,for语句就会自动的通过__iter__()方法来获得迭代器对象,并且通过next()方法来获取下一个元素。在Python中,for循环可以用于Python中的任何类型,包括列表、元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器。
使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。
本质上说迭代器是个对象,但是这个对象有个特殊的方法next()(在python3中使用__next__()代替了next方法)。当使用for循环来遍历整个对象时候,就会自动调用此对象的__next__()方法并获取下一个item。当所有的item全部取出后就会抛出一个StopIteration异常,这并不是错误的发生,而是告诉外部调用者迭代完成了,外部的调用者尝试去捕获这个异常去做进一步的处理。
不过迭代器是有限制的,例如
不能向后移动
不能回到开始
也无法复制一个迭代器。
因此要再次进行迭代只能重新生成一个新的迭代器对象。
举个栗子比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:
for line in open("test.txt").readlines():
print line
这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。
利用file的迭代器,我们可以这样写:
for line in open("test.txt"): #use file iterators
print line
这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。
python迭代工具模块[ Python - itertools模块 ]皮皮Blog
迭代器和可迭代对象 自定义迭代器 示例1普通的迭代器只能迭代一轮,一轮之后重复调用是无效的。解决这种问题的方法是,你可以定义一个可迭代的容器类:
class LoopIter(object):
def __init__(self, data):
self.data = data
# 必须在 __iter__ 中 yield 结果
def __iter__(self):
for index, letter in enumerate(self.data):
if letter == 'a':
yield index
这样的话,将类的实例迭代重复多少次都没问题:
for _ in indexs:
print(_)
# loop 1
print('loop 2')
for _ in indexs:
print(_)
但要注意的是,仅仅是
实现 __iter__ 方法的迭代器,只能通过 for 循环来迭代;想要通过 next 方法迭代的话则需要使用 iter 方法
:
next ( indexs ) # TypeError: 'LoopIter' object is not an iterator
iter_indexs = iter ( indexs )
next ( iter_indexs )# 8
示例2
下面例子中实现了一个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很类似。
在上面的例子中,myRange这个对象就是一个可迭代对象,同时它本身也是一个迭代器对象。
对于一个可迭代对象,如果它本身又是一个迭代器对象,就没有办法支持多次迭代 。(iter返回的是本身self)

为了解决上面的问题,可以分别定义可迭代类型对象和迭代器类型对象;然后可迭代类型对象的__iter__()方法可以获得一个迭代器类型的对象。
分开定义的好处在于, 当对可迭代对象使用iter()转变时,返回一个新的迭代器对象, 这时不受先前产生的相应迭代器对象影响。
看下面的实现:
class Zrange: def __init__(self, n): self.n = n def __iter__(self): return ZrangeIterator(self.n) class ZrangeIterator: def __init__(self, n): self.i = 0 self.n = n def __iter__(self): return self def next(self): if self.i < self.n: i = self.i self.i += 1 return i else: raise StopIteration() zrange = Zrange(3) print zrange is iter(zrange) print [i for i in zrange] print [i for i in zrange]代码的运行结果为:

其实,通过下面代码可以看出,list类型也是按照上面的方式,list本身是一个可迭代对象,通过iter()方法可以获得list的迭代器对象:

zrange = Zrange(3)
print zrange is iter(zrange)
#>>> True
print [i for i in zrange] #>>>[1,2,3] print [i for i in zrange] #>>>[1,2,3]# 若不区分可迭代对象和迭代器, 即这里列表生成式中使用ZrangeIterator的话, 第二次调用时迭代器已被迭代完,第二次会为空集.
zzrange=ZrangeIterator(3);
print [i for i in zzrange] #>>>[1,2,3] print [i for i in zzrange] #>>>[]皮皮Blog
二、生成器(constructor)在Python中,使用生成器可以很方便的支持迭代器协议。生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。
生成器函数在Python中与迭代器协议的概念联系在一起。简而言之,包含yield语句的函数会被特地编译成生成器。 当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口。
函数也许会有个return语句,但它的作用是用来yield产生值的。不像一般的函数会生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行和状态,他的本地变量将保存状态信息,这些信息在函数恢复时将再度有效。
生成器的使用

例子中定义了一个生成器函数,函数返回一个生成器对象,然后就可以通过for语句进行迭代访问了。
其实,生成器函数返回生成器的迭代器。 “生成器的迭代器”这个术语通常被称作”生成器”。要注意的是生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法,其中一个就是next()。如同迭代器一样,我们可以使用next()函数来获取下一个值。
Note: 这里的函数Zrange必须要生成实例,不能使用Zrange(3).__next__()形式调用,这相当于每次新生成了一个Zrange对象,这样里面的循环每次调用都只会循环一次!
生成器执行流程
生成器是怎么工作的?从上面的例子也可以看到,生成器函数跟普通的函数是有很大差别的。加入一些打印信息,进一步看看生成器的执行流程:

通过结果可以看到:
当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有 执行。当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止
next()方法的返回值就是yield语句处的参数(yielded value) 当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常 皮皮Blog Generator expressions生成器表达式为什么要有生成器表达式
列表解析可能出现的问题:List comprehensions have one possible problem, and that is they build the list in memory right away. If your dealing with big data sets, that can be a big problem, but even with small lists, it is still extra overhead that might not be needed.
If you are only going to loop over the results once it has no gain in building this list. So if you can give up being able to index into the result, and do other list operations, you can use a generator expression, which uses very similar syntax, but creates a lazy object, that computes nothing until you ask for a value.
列表推导也可能会有一些负面效应,那就是整个列表必须一次性加载于内存之中,这对上面举的例子而言不是问题,甚至扩大若干倍之后也都不是问题。但是总会达到极限,内存总会被用完。
针对上面的问题,生成器(Generator)能够很好的解决。生成器表达式不会一次将整个列表加载到内存之中,而是生成一个生成器对象(Generator objector),所以一次只加载一个列表元素。
除非特殊的原因,应该经常在代码中使用生成器表达式。但除非是面对非常大的列表,否则是不会看出明显区别的。
# generator expression for the square of all the numbers squares = (num * num for num in numbers) # where you would likely get a memory problem otherwise with open('/some/number/file', 'r') as f: squares = (int(num) * int(num) for num in f) # do something with these numbers生成器表达式是在python2.4中引入的,当序列过长, 而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析。和列表解析一样,只不过生成器表达式是被()括起来的: ( expr for iter_var in iterable if cond_expr )
生成器表达式并不是创建一个列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目”产生”(yield)出来。 生成器表达式使用了”惰性计算”(lazy evaluation)