《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 协议 ,转载请注明出处!