专项练习 · 9 分钟阅读

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])

在边界转成列表

你的练习清单

常见问题

groupby 会排序数据吗?

不会。它只分组相邻元素。相同键可能分散时,先按同一个键排序。

chain 可以拼接字符串吗?

可以。它会像 for 循环一样,逐个字符遍历每个字符串。

为什么 itertools 结果是空的?

很可能迭代器已经被消费。迭代器只能使用一次;创建新的迭代器,或者先物化成列表。

练习 itertools

12 道题,覆盖 count、islice、chain、groupby 和惰性迭代器行为。

打开练习

继续阅读: