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

迭代器(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 val
except StopIteration:
    pass
#输出
0
1
2
3
4

注意: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

#输出:
0
1
2
3
4
发布了93 篇原创文章 · 获赞 97 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/duxu24/article/details/53787717