迭代器
可以用在 for
语句进行循环的对象就是可迭代对象。除了内置的数据类型(列表、元组、字符串、字典等)可以通过 for
语句进行迭代,我们也可以自己创建一个容器,包含一系列元素,可以通过 for
语句依次循环取出每一个元素,这种容器就是迭代器(iterator)。除了用 for
遍历,迭代器还可以通过 next()
方法逐一读取下一个元素。要创建一个迭代器有3种方法,其中前两种分别是:
- 为容器对象添加
__iter__()
和__next__()
方法(Python 2.7 中是next()
);__iter__()
返回迭代器对象本身self
,__next__()
则返回每次调用next()
或迭代时的元素; - 内置函数
iter()
将可迭代对象转化为迭代器
li = [1,2] it = iter(li) print(it) print(it.__next__()) print(it.__next__())创建迭代器对象的好处是当序列长度很大时,可以减少内存消耗,因为每次只需要记录一个值即刻。
生成器
前面说到创建迭代器有3种方法,其中第三种就是生成器(generator)。生成器通过
yield
语句快速生成迭代器,省略了复杂的 __iter__()
& __next__()
方式:def gen(): yield 5 yield "Hello" yield "World" yield 4 for i in gen(): print(i)
简单来说,
yield
语句可以让普通函数变成一个生成器,并且相应的 __next__()
方法返回的是 yield
后面的值。一种更直观的解释是:程序执行到 yield
会返回值并暂停,再次调用 next()
时会从上次暂停的地方继续开始执行:def Zrange(n): print("beginning of Zrange") i = 0 while i < n: print("before yield", i) yield i i += 1 print("after yield", i) print("endding of Zrange") zrange = Zrange(3) print("------------") print(zrange.__next__()) print("------------") print(zrange.__next__()) print("------------") print(zrange.__next__()) print("------------") print(zrange.__next__()) print("------------")
结果如下所示
------------ beginning of Zrange before yield 0 0 ------------ after yield 1 before yield 1 1 ------------ after yield 2 before yield 2 2 ------------ after yield 3 endding of Zrange
通过结果可以看到:
- 当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有 执行。
-
当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止
- next()方法的返回值就是yield语句处的参数(yielded value)
- 当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。
总结
本文介绍了Python迭代器和生成器的相关内容。
- 通过实现迭代器协议对应的__iter__()和next()方法,可以自定义迭代器类型。对于可迭代对象,for语句可以通过iter()方法获取迭代器,并且通过next()方法获得容器的下一个元素。
- 像列表这种序列类型的对象,可迭代对象和迭代器对象是相互独立存在的,在迭代的过程中各个迭代器相互独立;但是,有的可迭代对象本身又是迭代器对象,那么迭代器就没法独立使用。
- itertools模块提供了一系列迭代器,能够帮助用户轻松地使用排列、组合、笛卡尔积或其他组合结构。
- 生成器是一种特殊的迭代器,内部支持了生成器协议,不需要明确定义__iter__()和next()方法。
- 生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果。
参考资料
http://python.jobbole.com/81881/
http://python.jobbole.com/84527/