Python 迭代器 & __iter__方法
转载自 http://blog.csdn.net/bluebird_237/article/details/38894617
迭代器就是重复地做一些事情,可以简单的理解为循环,在python中实现了__iter__方法的对象是可迭代的,实现了next()方法的对象是迭代器,这样说起来有点拗口,实际上要想让一个迭代器工作,至少要实现__iter__方法和next方法。很多时候使用迭代器完成的工作使用列表也可以完成,但是如果有很多值列表就会占用太多的内存,而且使用迭代器也让我们的程序更加通用、优雅、pythonic。
如果一个类想被用于for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()
方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def next(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration();
return self.a # 返回下一个值
现在,试试把Fib实例作用于for循环:
>>> for n in Fib():
... print n
...
1
1
2
3
5
...
46368
75025
迭代器是一个对象,而生成器是一个函数,迭代器和生成器是python中两个非常强大的特性,编写程序时你可以不使用生成器达到同样的效果,但是生成器让你的程序更加pythonic。创建生成器非常简单,只要在函数中加入yield语句即可。函数中每次使用yield产生一个值,函数就返回该值,然后停止执行,等待被激活,被激活后继续在原来的位置执行。下边的例子实现了同样的功能:
- #!/usr/bin/env python
- #coding=utf-8
- def fib():
- a,b = 0,1
- while 1:
- a,b = b,a+b
- yield a
- for f in fib():
- if f < 10000:
- print f
- else:
- break
如何迭代?
根本上说, 迭代器就是有一个 next() 方法的对象, 而不是通过索引来计数. 当你或是一个循环机制(例如 for 语句)需要下一个项时, 调用迭代器的 next() 方法就可以获得它. 条目全部取出后, 会引发一个 StopIteration 异常, 这并不表示错误发生, 只是告诉外部调用者, 迭代完成.
不过, 迭代器也有一些限制. 例如你不能向后移动, 不能回到开始, 也不能复制一个迭代器.如果你要再次(或者是同时)迭代同个对象, 你只能去创建另一个迭代器对象. 不过, 这并不糟糕,因为还有其他的工具来帮助你使用迭代器.
reversed() 内建函数将返回一个反序访问的迭代器. enumerate() 内建函数同样也返回迭代器.另外两个新的内建函数, any() 和 all() , 在 Python 2.5 中新增, 如果迭代器中某个/所有条目的值都为布尔真时,则它们返回值为真. 本章先前部分我们展示了如何在 for 循环中通过索引或是可迭代对象来遍历条目. 同时 Python 还提供了一整个 itertools 模块, 它包含各种有用的迭代器.
迭代器工作原理
如果这是一个实际应用程序, 那么我们需要把代码放在一个 try-except 块中. 序列现在会自
动地产生它们自己的迭代器, 所以一个 for 循环:
- for i in seq:
- do_something_to(i)
实际上是这样工作的:
- fetch = iter(seq)
- while True:
- try:
- i = fetch.next()
- except StopIteration:
- break
- do_something_to(i)
另外, Python 还引进了三个新的内建字典方法来定义迭代: myDict.iterkeys() (通过 keys 迭
代), myDict.itervalues() (通过 values 迭代), 以及 myDicit.iteritems() (通过 key/value 对来迭代). 注意, in 操作符也可以用于检查字典的 key 是否存在 , 之前的布尔表达式myDict.has_key(anyKey) 可以被简写为 anyKey in myDict .
===文件===
文件对象生成的迭代器会自动调用 readline() 方法. 这样, 循环就可以访问文本文件的所有
行. 程序员可以使用 更简单的 for eachLine in myFile 替换 for eachLine in myFile.readlines() :
- >>>myFile=open(‘config-win.txt’)
- >>> for eachLine in myFile:
- … print eachLine, # comma suppresses extra n
- …
- [EditorWindow]
- font-name: courier new
- font-size: 10
- >>> myFile.close()
可变对象和迭代器
记住,在迭代可变对象的时候修改它们并不是个好主意. 这在迭代器出现之前就是一个问题.
一个流行的例子就是循环列表的时候删除满足(或不满足)特定条件的项:
- for eachURL in allURLs:
- if not eachURL.startswith(‘http://’):
- allURLs.remove(eachURL) # YIKES!!
除列表外的其他序列都是不可变的, 所以危险就发生在这里. 一个序列的迭代器只是记录你当前到达第多少个元素, 所以如果你在迭代时改变了元素, 更新会立即反映到你所迭代的条目上.在迭代字典的 key 时, 你绝对不能改变这个字典. 使用字典的 keys() 方法是可以的, 因为keys() 返回一个独立于字典的列表. 而迭代器是与实际对象绑定在一起的, 它将不会继续执行下去:
- >>> myDict = {‘a’: 1, ‘b’: 2, ‘c’: 3, ‘d’: 4}
- >>> for eachKey in myDict:
- … print eachKey, myDict[eachKey]
- … del myDict[eachKey]
- … a 1
- Traceback (most recent call last):
- File “<stdin>”, line 1, in <module>
- RuntimeError: dictionary changed size during iteration
这样可以避免有缺陷的代码. 更多有关迭代器的细节请参阅 PEP 234 .
如何创建迭代器
对一个对象调用 iter() 就可以得到它的迭代器. 它的语法如下:
- iter(obj)
- iter(func, sentinel)
如果你传递一个参数给 iter() , 它会检查你传递的是不是一个序列, 如果是, 那么很简单:
根据索引从 0 一直迭代到序列结束. 另一个创建迭代器的方法是使用类, 我们将在第 13 章详细
介绍, 一个实现了 __iter__() 和 next() 方法的类可以作为迭代器使用.
如果是传递两个参数给 iter() , 它会重复地调用 func , 直到迭代器的下个值等于sentinel .