Python 迭代器(iterator)

1》

迭代器是一个实现了迭代器协议的对象。Python中的迭代器协议就是具有next方法的对象会前进到下一结果,而在一系列结果的末尾则会引发StopIteration异常。任何这种类型的对象在Python中都可以用for循环或其他遍历工具进行迭代,迭代工具内部会在每次迭代时调用next方法,并且通过捕捉StopIteration异常来结束迭代。使用迭代器一个显而易见的好处就是:迭代器提供了一个统一的访问序列的接口,只要是实现了__iter__()方法的对象,就可以使用迭代器通过调用next()方法每次只从对象中读取一条数据,不会造成内存的过大开销。

比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:
f=open(r'C:\Users\Administrator\Desktop\wahaha.txt','r')
for line in f.readlines():
    print line[:-1] #去掉每行末尾的换行符
运行结果:
love python
love python2
love python3
这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。


利用file的迭代器,我们可以这样写:
f=open(r'C:\Users\Administrator\Desktop\wahaha.txt','r')
for line in f: #使用文件迭代器
    print line[:-1] #去掉每行末尾的换行符
运行结果:
love python
love python2
love python3
这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。这就是迭代器的一个显著的优点,它不要求我们事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得迭代器特别适合用于遍历一些巨大的或是无限的序列,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值(Lazy evaluation)

2》

对于支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。迭代器不能回退,只能往前进行迭代。迭代器不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作(保守的方法是坚持使用不可变的集合)。


使用内建的工厂函数iter(iterable)可以获取迭代器对象:
>>> l
[0, 1, 2, 3, 4]
>>> it=iter(l)
>>> it
<listiterator object at 0x069F6C50>
使用迭代器的next()方法可以访问下一个元素:
>>> it.next()
0
>>> it.next()
1
>>> it.next()
2
如果是Python 2.6+,还有内建函数next(iterator)可以完成这一功能:
>>> next(it)
3
>>> next(it)
4

如何判断迭代器是否有更多的元素可以访问呢?如果我们已经访问到了最后一个元素,再使用next()方法会怎样呢?
>>> it.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Python遇到这样的情况时将会抛出StopIteration异常。事实上,Python正是根据是否检查到这个异常来决定是否停止迭代的。 

了解了这些情况以后,我们就能使用迭代器进行遍历了。
>>> l
[0, 1, 2, 3, 4]
>>> it=iter(l)
>>> try:
...     while True:
...         item=it.next()
...         print item
... except StopIteration:
...      pass
... 
0
1
2
3
4

实际上,因为迭代操作如此普遍,Python专门将关键字for用作了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。上述代码可以写成如下我们非常熟悉的形式:
>>> l
[0, 1, 2, 3, 4]
>>> for item in l:
...     print item,
... 
0 1 2 3 4
首先Python将对关键字in后的对象调用iter函数获取迭代器,然后调用迭代器的next方法获取元素,直到抛出StopIteration异常。对迭代器调用iter函数时将返回迭代器自身,所以迭代器也可以用于for语句中,不需要特殊处理。

>>> l
[2, 3, 4]
>>> it=iter(l)
>>> it
<listiterator object at 0x030B5E50>
>>> itt=iter(it)
>>> itt
<listiterator object at 0x030B5E50>

3》

常用的几个内建数据结构tuple、list、set、dict都支持迭代器,字符串也可以使用迭代操作。我们可以自己实现一个迭代器,只需要在类的__iter__方法中返回一个对象,这个对象需要拥有一个next()方法,这个方法能在恰当的时候能够抛出StopIteration异常即可。并不是必须要抛出异常,不抛出异常的迭代器将进行无限迭代,某些情况下这样的迭代器很有用,此时,我们需要自己判断元素并中止,否则就死循环了!

使用迭代器的循环可以避开索引,但有时候我们还是需要索引来进行一些操作的。这时候内建函数enumerate就派上用场咯,它能在iter函数的结果前加上索引,以元组返回,用起来就像这样:
>>> l
[3, 4, 5, 6]
>>> for i,v in enumerate(l):
...     print i,v
... 
0 3
1 4
2 5
3 6


友情链接:

关于iter( )函数的使用,参考:python iter( )函数

关于enumerate( )函数的使用,参考: Python enumerate类


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值