【Python那些事儿】Python中的迭代器

来源:互联网 发布:算法的乐趣 pdf 编辑:程序博客网 时间:2024/05/17 22:18

迭代器(iterator)协议

  • 对象必须提供一个next()方法,执行该方法时,要么返回迭代中的下一项,要么引起一个StopIteration异常。
  • 只能往前访问,不会后退。
  • 在Python中,支持迭代器协议就是实现对象的__iter____next__()方法。
    • __iter__()方法:返回迭代器对象本身;
    • __next__()方法:返回容器中的下一个元素,在结尾时引发StopIteration异常终止迭代器。

可迭代对象(iterable)

  • 实现了迭代器协议的对象,就是可迭代对象。
  • 如何实现迭代器协议:对象内部定义了一个__iter__()方法。
  • 在Python中,list、tuple、dict、set以及生成器对象都是可迭代对象。如果不确定哪个可迭代,可以使用内置函数iter()测试。
>>> iter([6,8,10])#返回listterator对象<listiterator object at 0x7f992a891210>>>> iter({'a':1,'b':2})#返回dictionary-keyiterator对象<dictionary-keyiterator object at 0x7f992a880838>>>> iter(666)#不是可迭代对象Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: 'int' object is not iterable
  • 所有的iterable均可以通过内置函数iter()转化为iterator。

迭代器

  • 迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完结束,迭代器只能往前不能后退。
  • 迭代器有两个基本方法:iter、text方法。
    • 内置函数iter()next(),本质上都是用的对象的__iter__()__next__()的方法。

迭代器优点

  • 对于支持随机访问的数据结构:list、tuple等,迭代器和经典的for循环(索引访问)相比,并无优势,反而失去了索引值。不过可以使用内置函数enumerate()找回这个索引值。但对于无法随机访问的数据结构set(),迭代器是唯一的访问元素的方式。
  • 省内存:迭代器不需要事先准备好整个迭代过程中的所有元素,仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或销毁。这也是迭代器的一大优点:适合用于遍历一个巨大的或无限的集合,比如几个G的文件。下面用斐波那契数列为例:
    • 代码一直接在fab(max)中用print打印会导致函数的可复用性变差,因此fab返回None。其他函数无法获得fab函数返回的数列;
    • 代码二满足了可复用性的需求,但是占用了内存空间;
    • 代码三Fabs类通过next()不断返回数列的下一个,内存占用始终为常数。
#代码一:def fab(max):     L = []    n, a, b = 0, 0, 1     while n < max:         L.append(b)         a, b = b, a + b         n = n + 1    return L#代码二:  def fab(max):     n, a, b = 0, 0, 1     while n < max:         print b         a, b = b, a + b         n = n + 1#代码三:class Fab(object):     def __init__(self, max):         self.max = max         self.n, self.a, self.b = 0, 0, 1     def __iter__(self):         return self     def next(self):         if self.n < self.max:             r = self.b             self.a, self.b = self.b, self.a + self.b             self.n = self.n + 1             return r         raise StopIteration()

使用迭代器

  • 使用内置函数iter(iterable)获取迭代器对象:
>>> iter(range(5))<listiterator object at 0x7f992a8911d0>>>> iter(xrange(5))<rangeiterator object at 0x7f992a892240>
  • 使用next()方法访问下一个元素:
>>> a.next()0>>> a.next()1>>> a.next()2>>> a.next()3
  • 迭代器越界会抛出StopIteration异常:
>>> a.next()4>>> a.next()Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration

处理方法:

lst = range(5)it = iter(lst)try:    while True:        val = it.next()        print valexcept StopIteration:    pass#输出01234

注意:Python专门为for关键字做了迭代器的语法糖:在for循环中,Python将自动调用内置函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。

自定义迭代器

下面例子中实现了一个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()myRange = MyRange(5)for i in myRange:    print i#输出:01234
0 0