在学习python的资料中,经常看到迭代器和生成器,这样的名词。但到底什么是迭代器,什么是生成器?为什么会出现这样的类型,他们有什么便捷之处?
简单的讲就是循环操作,重复某一个过程很多次。可以用for循环对可迭代对象进行迭代。
常见的迭代对象有序列,字典和文件等,另外还有其他的对象。这些可迭代对象的特点是:实现了__iter__方法。例如:
>>> alist=[1,2,3] >>> lst=alist.__iter__() >>> type(lst) <type 'listiterator'> >>> atuple=(1,2,3) >>> tup=atuple.__iter__() >>> type(tup) <type 'tupleiterator'> >>> afile=open(r"e:\test.txt",'r') >>> fl=afile.__iter__() >>> type(fl) <type 'file'> >>> astr='abc' >>> string=astr.__iter__() Traceback (most recent call last): File "<pyshell#62>", line 1, in <module> string=astr.__iter__() AttributeError: 'str' object has no attribute '__iter__' >>> iter(astr) <iterator object at 0x0212BE70> >>> string=iter(astr) >>> anum=1 >>> iter(anum) Traceback (most recent call last): File "<pyshell#66>", line 1, in <module> iter(anum) TypeError: 'int' object is not iterable >>> type(string) <type 'iterator'> >>>
我们注意到,__iter__方法返回的对象就是迭代器对象。对于字符串astr来说,没有__iter__方法,但是可以用内建函数iter()来调用,所以是可迭代对象。而对于数字anum来说,iter()无法调用,所以是不可迭代的对象。因此可以用iter()来判断一个对象是否是可迭代对象。
内建函数iter()和方法__iter__的关系:
>>> help(list.__iter__) Help on wrapper_descriptor: __iter__(...) x.__iter__() <==> iter(x) >>>
迭代器就是有一个 next() 方法的对象。
在Python3.0中迭代器规则有些变化,迭代器对象应该实现__next__方法,而不是next。而新的内建函数next可以用于访问这个方法。换句话说,next(it)等同于3.0之前版本中的it.next()。
看看上面的迭代器是否都有next():
>>> lst.next() 1 >>> tup.next() 1 >>> fl.next() '\xef\xbb\xbfHello world!\n' >>> string.next() 'a'
在后台,for 语句在容器对象中调用iter().该函数返回一个定义了next()方法的迭代器对象,它在容器中逐一访问元素。没有后续元素时,next()就会抛出一个 StopIteration 异常, 这并不表示错误发生, 只是通知外部调用者,for语句循环结束, 迭代完成。
以下是其工作原理的示例:
>>> s='abc' >>> it=iter(s) >>> it <iterator object at 0x0228F9D0> >>> it.next() 'a' >>> it.next() 'b' >>> it.next() 'c' >>> it.next() Traceback (most recent call last): File "<pyshell#33>", line 1, in <module> it.next() StopIteration >>>
了解了迭代器协议的后台机制,就可以很容易的给自己的类添加迭代器行为。定义一个 __iter__() 方法,使其返回一个带有 next() 方法的对象。如果这个类已经定义了 next(),那么 __iter__() 只需要返回self():
>>> class Reversestr: 'Iterator for looping over a swquence backwards' def __init__(self,data): self.data = data self.index = len(data) def __iter__(self): return self def next(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index] >>> for char in Reversestr('spam'): print char m a p s >>>
如果有一个可迭代计算值的函数,那么在使用时可能只需要获得指定的值,而不需要获得所有值的列表。这时候,如果数值很多,使用列表会占用太多的内存。当然,使用迭代器更简单更优雅。
看一个不使用列表的例子,因为那样列表的长度必须无限。
>>> class Fibs(): def __init__(self): self.a=0 self.b=1 def next(self): self.a,self.b = self.b,self.a + self.b return self.a def __iter__(self): return self >>>
构造一个Fibs对象:
>>> fibs = Fibs()在for循环中使用该对象——比如查找在斐波那契数列中比500大的最小的数:
>>> for f in fibs: if f > 500: print f break 610因为设置了break,所以循环停止了,否则会一直继续下去。
内建函数iter可以从可迭代对象中获取迭代器,以list为例:
>>> a=[1,2,3] >>> type(a) <type 'list'> >>> it=iter(a) >>> type(it) <type 'listiterator'> >>> it.next() 1 >>> it.next() 2 >>> it.next() 3 >>> it.next() Traceback (most recent call last): File "<pyshell#140>", line 1, in <module> it.next() StopIteration >>>
注:iter(a)和a.__iter__()等价。
反过来,使用list构造方法可以将迭代器转化为列表:
>>> a=[1,2,3] >>> it=iter(a) >>> list(it) [1, 2, 3] >>>
可迭代对象:一个实现了__iter__方法的对象。
迭代器:一个实现了next方法的对象。
关系:__iter__方法返回一个迭代器。