《Fluent Python》笔记 迭代器和生成器
迭代器用于从集合中取出元素; 而生成器用于“凭空”生成元素。
可迭代对象
解释器需要迭代对象 x 时, 会自动调用 iter(x)。
内置的 iter 函数有以下作用:
(1) 检查对象是否实现了 __iter__ 方法, 如果实现了就调用它, 获取一个迭代器对象。
(2) 如果没有实现 __iter__ 方法, 但是实现了 __getitem__ 方法,Python 会创建一个迭代器, 尝试按顺序(从索引 0 开始) 获取元素。
(3) 如果尝试失败, Python 抛出 TypeError 异常, 通常会提示“C object is not iterable”(C 对象不可迭代), 其中 C 是目标对象所属的类。 
正因为序列都实现了 __getitem__ 方法,所以序列对象可迭代。如果实现了__iter__方法,能够给返回对象自身的迭代器,那么这个对象也是可迭代对象。
可迭代对象与迭代器的关系:Python从可迭代对象中获取迭代器。
迭代器
使用iter()函数从可迭代对象a中构建迭代器b:b = iter(a)。
使用next()函数使用迭代器b:next(b)。
迭代器对象中实现了__next__方法,返回可迭代对象中的下一个元素;如果没有元素了,那么抛出 StopIteration 异常。迭代器还实现了 __iter__ 方法, 因此迭代器也可以迭代。  
下面示例展示了典型的迭代器模式实现一个可迭代对象(Sentence类对象)。
| 1 |  | 
可迭代的对象一定不能是自身的迭代器。即可迭代的对象必须实现 __iter__ 方法, 但不能实现 __next__ 方法。另一方面, 迭代器应该一直可以迭代。 迭代器的 __iter__ 方法应该返回自身。
生成器函数
使用生成器函数实现和上面迭代器示例一样的功能:
| 1 |  | 
上面代码中__iter__方法的定义体中有yield关键字,即__iter__方法是一个生成器函数。调用生成器函数时,会返回一个生成器对象。
只要 Python 函数中包含关键字 yield, 该函数就是生成器函数。
生成器是迭代器(因为生成器完全实现了迭代器接口), 会生成传给 yield 关键字的表达式的值。对于生成器函数创建的生成器对象, 我们可以理解为其包装了生成器函数的定义体。 把生成器传给 next() 函数时, 生成器函数会向前, 执行函数定义体中的下一个 yield 语句, 返回产出的值, 并在函数定义体的当前位置暂停。 最终, 函数的定义体返回时, 外层的生成器对象会抛出StopIteration 异常——这一点与迭代器协议一致。  
当通过
for循环进行迭代时,for机制的作用与g = iter(gen_AB())(其中gen_AB为生成器函数名)一样, 用于获取生成器对象, 然后每次迭代时调用next(g)。
生成器表达式
生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。因此,使用生成器表达式构建列表更加节省内存。
在前面的序列类型提到,列表推导两边是方括号,而生成器表达式两边是圆括号。
生成器表达式是构建一个生成器对象,和生成器函数返回的生成器对象一样。
标准库中的生成器函数
- 用于过滤的生成器函数 - 模块 - 函数 - 说明 - itertools - compress(it, selector_it) - 并行处理两个可迭代的对象; 如果 selector_it 中的元素是真值, 产出 it 中对应的元素。 - itertools - dropwhile(predicate, it) - 处理 it, 跳过 predicate 的计算结果为真值的元 素, 然后产出剩下的各个元素(不再进一步检 查)。 - ( 内置) - filter(predicate, it)- 把 it 中的各个元素传给 predicate, 如果 predicate(item)返回真值, 那么产出对应的元 素; 如果predicate是 None, 那么只产出真值元素。 - itertools - filterfalse(predicate, it) - 与 filter 函数的作用类似, 不过 predicate 的 逻辑是相反的: predicate 返回假值时产出对应 的元素。 - itertools - islice(it, stop) 或 islice(it, start, stop, step=1) - 产出 it 的切片, 作用类似于 s[:stop] 或 s[start:stop:step], 不过 it 可以是任何可迭代 的对象, 而且这个函数实现的是惰性操作。 - itertools - takewhile(predicate, it) - predicate 返回真值时产出对应的元素, 然后立 即停止, 不再继续检查。 
- 用于映射的生成器函数 - 模块 - 函数 - 说明 - itertools - accumulate(it, [func]) - 产出累积的总和;如果提供了func,那么把前两个元素传给它,然后把计算结果和下一个元素传给它, 以此类推,最后产出结果。 - (内置) - enumerate(iterable, start=0)- 产出由两个元素组成的元组,结构是 (index, item), 其中 index 从 start 开始计数,item 则从 iterable 中获取。 - (内置) - map(func, it1, [it2, ..., itN])- 把 it 中的各个元素传给func,产出结果;如果传入 N 个可迭代的对象,那么 func 必须能接受 N 个参 数, 而且要并行处理各个可迭代的对象。 - itertools - starmap(func, it) - 把 it 中的各个元素传给 func,产出结果;输入的可迭代对象应该产出可迭代的元素 iit, 然后以 func(*iit) 这种形式调用 func。 
- 合并多个可迭代对象的生成器函数 - 模块 - 函数 - 说明 - itertools - chain(it1, …, itN) - 先产出 it1 中的所有元素, 然后产出 it2 中的所有元素, 以此类推,无缝连接在一起。 - itertools - chain.from_iterable(it) - 产出 it 生成的各个可迭代对象中的元素 一个接一个,无缝连接在一起; it 应该产出可迭代的元素,例如可迭代的对象列表。 - itertools - product(it1, …, itN, repeat=1) - 计算笛卡儿积:从输入的各个可迭代对象中获取元素,合并成由 N 个元素组成的元组, 与嵌套的 for 循环效果一样; repeat 指明重复处理多少次输入的可迭代对象。 - (内置) - zip(it1, ..., itN)- 并行从输入的各个可迭代对象中获取元素,产出由 N 个元素组成的元组,只要有一个可迭代的对象到头了,就默默地停止。 - itertools - zip_longest(it1, …, itN, fillvalue=None) - 并行从输入的各个可迭代对象中获取元素, 产出由 N 个元素组成的元组,等到最长的可迭代对象到头后才停止, 空缺的值使用 fillvalue 填充。 
- 把输入的各个元素扩展成多个输出元素的生成器函数 - 模块 - 函数 - 说明 - itertools - combinations(it, out_len)- 把 it 产出的 out_len 个元素组合在一起,然后产出。 - itertools - combinations_with_replacement(it, out_len)- 把 it 产出的 out_len 个元素组合在一起,然后产出,包含相同元素的组合。 - itertools - count(start=0, step=1) - 从 start 开始不断产出数字, 按 step 指定的步幅增加。 - itertools - cycle(it) - 从 it 中产出各个元素,存储各个元素的副本, 然后按顺序重复不断地产出各个元素。 - itertools - permutations(it, out_len=None)- 把 out_len 个 it 产出的元素排列在一起, 然后产出这些排列;out_len的默认值等于 len(list(it))。 - itertools - repeat(item, [times])- 重复不断地产出指定的元素, 除非提供 times, 指定次数。 
- 用于重新排列元素的生成器函数 - 模块 - 函数 - 说明 - itertools - groupby(it,key=None) - 产出由两个元素组成的元素, 形式为 (key, group),其中 key 是分组标准, group 是生成器,用于产出分组里的元素。 - (内置) - reversed(seq)- 从后向前,倒序产出 seq 中的元素; seq 必须是序列,或者是实现了 reversed 特殊方法的对象。 - itertools - tee(it, n=2) - 产出一个由 n 个生成器组成的元组, 每个生成器用于单独产出输入的可迭代对象中的元素。 
上述生成器函数,其参数是生成器对象,返回也是生成器对象,故可以组合使用。
可迭代的归约函数
如果一个函数接受一个可迭代的对象,然后返回单个结果,那么就可以叫这个函数为“归约”函数、 “合拢”函数或“累加”函数。
常见的归约函数如下:
| 模块 | 函数 | 说明 | 
|---|---|---|
| (内置) | all(it) | it 中的所有元素都为真值时返回 True,否则返回 False; all([]) 返回 True。 | 
| (内置) | any(it) | 只要 it 中有元素为真值就返回 True, 否则返回 False; any([]) 返回 False。 | 
| (内置) | max(it, [key=,] [default=]) | 返回 it 中值最大的元素; *key 是排序函数,与 sorted 函 数中的一样;如果可迭代的对象为空,返回 default。 | 
| (内置) | min(it, [key=,] [default=]) | 返回 it 中值最小的元素; #key 是排序函数,与 sorted 函 数中的一样;如果可迭代的对象为空,返回 default。 | 
| functools | reduce(func, it, [initial]) | 把前两个元素传给 func,然后把计算结果和第三个元素传 给 func,以此类推,返回最后的结果;如果提供了 initial,把它当作第一个元素传入。 | 
| (内置) | sum(it, start=0) | it 中所有元素的总和, 如果提供可选的 start,会把它加 上(计算浮点数的加法时, 可以使用 math.fsum 函数提高 精度)。 | 
iter函数的特殊用法
Python 中迭代对象 x 时会调用 iter(x)得到一个迭代器对象。除此之外,iter函数还有一个用法,示例代码如下:
| 1 |  | 
这里iter函数传入两个参数, 使用常规的函数或任何可调用的对象创建迭代器。 第一个参数必须是可调用的对象(fp.readline), 用于不断调用(没有参数)产出各个值; 第二个值是哨符('\n'), 这是个标记值,当可调用的对象返回的值等于哨符时(读取到空行或文件末尾),触发迭代器抛出 StopIteration 异常(迭代结束), 而不产出这个值。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!