Python 的 itertools:别再手写循环做组合与分组
写 Python 时,很多循环其实是在重复造轮子:把几个列表拼成一个、取前 N 条、按某个键分组、生成所有两两组合。手写这些循环不难,但容易写错边界,也难一眼看出意图。itertools 就是标准库里专门干这件事的工具箱:它提供的都是惰性迭代器,用多少算多少,不会一次把结果全算出来占内存。
先看最常用的 chain,把多个可迭代对象首尾相接:
from itertools import chain
pages = [1, 2, 3]
more = [4, 5]
for n in chain(pages, more):
print(n) # 1, 2, 3, 4, 5
不用先 pages + more 拼出一个新列表,尤其当两边都是大文件或生成器时,chain 不复制、不占额外内存,读到哪算到哪。chain.from_iterable 还能把“列表的列表”拍平一层,处理分批读到的数据很顺手。
第二个顺手的是 islice,给迭代器切一刀。普通切片 data[10:20] 只对列表有效,生成器没有下标,切了就报错:
from itertools import islice
def lines():
with open("app.log") as f:
yield from f
first_ten = list(islice(lines(), 10))
islice 只往前走十步就停,不会把整个文件读进来。看日志前几行、取分页的第一页,都是它的活。注意它是有去无回的:切过的部分消费掉了,不能再倒回去。
分组用 groupby,但有一个前提常被忽略:它只把连续相同的元素分到一组,输入必须先按同一个键排好序:
from itertools import groupby
orders = [("北京", 120), ("上海", 80), ("北京", 90)]
orders.sort(key=lambda o: o[0])
for city, group in groupby(orders, key=lambda o: o[0]):
print(city, sum(o[1] for o in group))
没排序就直接 groupby,同一个城市会出现两次,分组结果就错了。排序是调用方的责任,groupby 不替你做。
最后是组合类工具,写测试数据和排列时很省事:
from itertools import combinations, product
list(combinations(["A", "B", "C"], 2))
# [('A', 'B'), ('A', 'C'), ('B', 'C')]
list(product([1, 2], ["x", "y"]))
# [(1, 'x'), (1, 'y'), (2, 'x'), (2, 'y')]
combinations 不考虑顺序、每个元素只用一次,适合“从 N 个里挑两个”的场景;product 是笛卡尔积,适合参数组合。两者返回的都是迭代器,组合数爆炸时也不会先把所有结果算出来撑爆内存——但这也意味着真要全量展开前,先想清楚数量级。
用 itertools 的思路就一句话:先想清楚你要的是拼接、切片、分组还是组合,找对应的工具,而不是一上来就写 for 循环加临时列表。循环写得越少,边界错得越少,读代码的人也越快明白你要干什么。
- 点赞
- 收藏
- 关注作者
评论(0)