早高峰的订单像一条长河,后厨却按十份一批做。itertools 可以切片,又不用复制整条河。哪个工具分组时不会丢掉余数?
itertools 为迭代器流水线提供小型积木。除非你主动要求,它们不会创建完整列表,因此很适合与生成器组合。先掌握 count、islice、chain 和 groupby,再用它们组合,而不是再写一个索引循环。 你还会累积总量、用 tee 复制迭代器,并构造组合。
不用 range 也能计数
count 会产生无限序列。配合 islice 只请求固定数量的值。islice 适用于任何可迭代对象,不只是列表,而且返回迭代器。
from itertools import count, islice
print(list(islice(count(10), 3)))给无限序列加上边界
用 chain 拼接可迭代对象
chain 会依次读取一个可迭代对象,再读取下一个,不构建中间列表。数据来自不同来源时很方便。只有需要打印或重复使用时,才用 list 包起来。
from itertools import chain
print(list(chain([1, 2], ["a", "b"])))把多段合成一个流
分组相邻的值
groupby 会把键相同的连续元素分到一组。它不会替你排序,所以相同键分散在各处时要先排序。分组结果是迭代器,必须在前进到下一个键之前消费完。
from itertools import groupby
data = [("a", 1), ("a", 2), ("b", 3)]
for key, group in groupby(data, key=lambda pair: pair[0]):
print(key, list(group))相同键的连续片段
accumulate 与 pairwise 派生流
accumulate 把值变成运行总量;pairwise 产出相邻对。两者都是惰性的,不用索引循环就能表达关系。累加器需要零这样的起点时,用 initial=。
from itertools import accumulate, pairwise
values = [4, 3, 2]
print(list(accumulate(values)))
print(list(pairwise(values)))
print(list(accumulate(values, initial=0)))运行总量与相邻对
tee 复制迭代器,但会缓冲
tee 在一个源上返回两个独立迭代器。它不是深拷贝:一个分支提前取走的值会为另一个分支缓冲。两个分支都要消费;数据小到可以直接存下时,优先用列表。
from itertools import tee
values = iter([1, 2, 3])
a, b = tee(values)
print(list(a))
print(list(b))
print(list(a))两个读者,一个带缓冲的源
组合与笛卡尔积
combinations 选出不重复、无序的组合;product 构造输入之间的所有配对。结果数量增长很快,所以输入要小。它们适合测试矩阵和小型搜索空间,不适合暴力枚举大数据。
from itertools import combinations, product
print(list(combinations(["a", "b", "c"], 2)))
print(list(product([1, 2], ["x", "y"])))小型组合积木
本课要点
- count 可以表示无限的等差序列。
- islice 可以从任何可迭代对象中截取有限前缀。
- chain 会把多个可迭代对象首尾相接。
- groupby 分组相邻元素;键分散时要先排序。
- itertools 函数返回迭代器,不是列表。
- accumulate 和 pairwise 派生惰性流;tee 会缓冲;组合数量增长很快。
常见错误
- 直接对 itertools 返回的迭代器做下标访问。
- 在前进 groupby 之前忘记消费当前分组。
- 对未排序数据使用 groupby,却期待全局分组。
- 对超大流使用 tee,却以为它完全不占内存。
打印 count(10) 的前三个值,再用 chain 拼接 [1, 2] 和 [3]。
动手试试
改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。
练习本课
12 道题,作答后立即看到解释。