Python itertools 练习指南:惰性数据流
itertools 是一组小型迭代器工具。它们不试图解决整个问题,而是可以组合。认识 count、islice、chain 和 groupby 之后,很多循环会变得更短、更容易读。
给无限流加上边界
count 会永远产生值,只有另一个函数限制请求数量时才有用。islice 截取前缀,而不把整个流变成列表。结果仍然是惰性的,需要查看时再包起来。
from itertools import count, islice
print(list(islice(count(10), 3)))有限的窗口
用 chain 拼接数据流
chain 按顺序读取输入,不会排序或去重。它可以直接替代“把一个列表追加到另一个列表”的循环。输入很大时,chain 不必预先构建合并后的列表。
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))连续分组
先排序再分组
分组键和排序键必须一致。稳定排序会保留相同键内部的原始顺序,这通常正是想要的结果。排序后,groupby 才能把相同键看成一个连续片段。
from itertools import groupby
data = [("b", 1), ("a", 2), ("a", 3)]
sorted_data = sorted(data, key=lambda pair: pair[0])
print([(key, len(list(group))) for key, group in groupby(sorted_data, key=lambda pair: pair[0])])先排序,再分组
有意识地物化
大多数 itertools 结果是迭代器,只能消费一次,也不支持下下标访问。需要打印、计数或多轮遍历时,再转换成列表。物化应发生在边界,而不是每个阶段中间。
from itertools import chain
result = list(chain([1], [2, 3]))
print(result)
print(result[1])在边界转成列表
你的练习清单
- 用 islice 给 count 或其他无限迭代器加边界。
- 用 chain 拼接可迭代对象,避免追加循环。
- groupby 之前按同一个键排序。
- 前进到下一组前先消费当前分组。
- 只有需要下标或复用时才转成列表。
常见问题
groupby 会排序数据吗?
不会。它只分组相邻元素。相同键可能分散时,先按同一个键排序。
chain 可以拼接字符串吗?
可以。它会像 for 循环一样,逐个字符遍历每个字符串。
为什么 itertools 结果是空的?
很可能迭代器已经被消费。迭代器只能使用一次;创建新的迭代器,或者先物化成列表。
练习 itertools
12 道题,覆盖 count、islice、chain、groupby 和惰性迭代器行为。
继续阅读: