迭代器是一个对象,而生成器是一个函数,迭代器和生成器是python中两个非常强大的特性,编写程序时你可以不使用生成器达到同样的效果,但是生成器让你的程序更加pythonic。创建生成器非常简单,只要在函数中加入yield语句即可。函数中每次使用yield产生一个值,函数就返回该值,然后停止执行,等待被激活,被激活后继续在原来的位置执行。下边的例子实现了同样的功能:
1 #!/usr/bin/env python 2 #coding=utf-8 3 def fib(): 4 a,b = 0,1 5 while 1: 6 a,b = b,a+b 7 yield a 8 for f in fib(): 9 if f < 10000: 10 print (f) 11 else: 12 break 13 14 15 1 16 1 17 2 18 3 19 5 20 8 21 13 22 21 23 34 24 55 25 89
如何迭代?
根本上说, 迭代器就是有一个 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 = seq.__iter__ #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, 'b': 3, 'd': 4} for eachKey in myDict: print (eachKey, myDict[eachKey]) del myDict[eachKey] ''' 结果: d 4 Traceback (most recent call last): File "test.py", line 2, in <module> for eachKey in myDict: RuntimeError: dictionary changed size during iteration '''
这样可以避免有缺陷的代码. 更多有关迭代器的细节请参阅 PEP 234 .
如何创建迭代器
对一个对象调用 iter() 就可以得到它的迭代器. 它的语法如下:
iter(obj)
iter(func, sentinel)
如果你传递一个参数给 iter() , 它会检查你传递的是不是一个序列, 如果是, 那么很简单:
根据索引从 0 一直迭代到序列结束. 另一个创建迭代器的方法是使用类, 我们将在第 13 章详细
介绍, 一个实现了 __iter__() 和 next() 方法的类可以作为迭代器使用.
如果是传递两个参数给 iter() , 它会重复地调用 func , 直到迭代器的下个值等于sentinel .