在处理列表、数据集或任何序列时,我们常常需要生成排列组合、过滤数据、分组聚合等操作。用普通循环写这些逻辑不仅代码冗长,还容易导致内存浪费(比如生成巨大的中间列表)。itertools 是 Python 标准库中专为高效迭代设计的模块,它提供了一套构建块(building blocks),让你能用声明式、内存友好的方式处理序列。
核心思想:迭代器即“惰性生产线”
itertools 里的函数几乎都返回迭代器,而不是一次性生成整个列表。这意味着数据是按需产出的——只有当你真正遍历时,元素才会被计算出来。这对处理大数据流或无限序列尤其重要,因为你不会因为内存不足而崩溃。
常用函数分类速览
为了方便记忆和选用,可以把 itertools 的函数分成三类:无限迭代器、有限迭代器(终止于最短输入)、组合迭代器。
1. 无限迭代器(需配合 break 或 islice 使用)
count(start, step):从start开始,步长为step的无限等差数列。模拟枚举索引或计数器。cycle(iterable):无限循环一个可迭代对象中的元素,周而复始。repeat(elem, n):重复一个元素,可指定次数;若省略n则无限重复。
示例:生成流水号
A001, A002, A003 ...可以用(f'A{i:03d}' for i in count(1))。
2. 有限迭代器(终止于最短输入序列)
accumulate(iterable, func):累加(或自定义累积运算)。默认是求和,也可以传入operator.mul做累乘等。- *
chain(iterables) /chain.from_iterable()**:把多个可迭代对象“串联”成一个,像拼接序列一样遍历。 compress(data, selectors):根据selectors中对应位置的布尔值过滤data,为True则保留。dropwhile(predicate, iterable)/takewhile(predicate, iterable):前置条件丢弃 / 保留,直到条件不满足为止。filterfalse(predicate, iterable):与filter相反,保留使predicate为False的元素。groupby(iterable, key):按连续相同的键分组(注意:需要先排序才能全局分组)。islice(iterable, start, stop, step):切片迭代器,返回一个迭代器的切片,类似列表切片但惰性。- *
zip_longest(iterables, fillvalue)**:类似zip,但以最长输入为准,缺失部分用fillvalue填充。
3. 组合迭代器(排列组合神器)
- *
product(iterables, repeat)**:笛卡尔积(嵌套循环的等价物)。 permutations(iterable, r):长度为r的排列(顺序有关)。combinations(iterable, r):长度为r的组合(顺序无关,不重复)。combinations_with_replacement(iterable, r):允许元素重复的组合。
实际开发中的典型场景
场景一:数据批处理分页
假设从数据库或 API 拉取大量 ID 列表,每次处理 100 条。使用 islice 可以优雅地切片迭代器,而不必一次性加载所有数据到内存。
from itertools import islice
def batch(iterable, size):
it = iter(iterable)
while True:
batch = list(islice(it, size))
if not batch:
break
yield batch
for chunk in batch(range(1000), 100):
# 处理每个 chunk(100条)
print(len(chunk))
场景二:生成多条件组合测试用例
接口测试时,你可能需要覆盖多个参数的所有取值组合,用 product 一键生成。
from itertools import product
methods = ['GET', 'POST']
versions = ['v1', 'v2']
envs = ['dev', 'staging', 'prod']
for combo in product(methods, versions, envs):
print(combo) # ('GET', 'v1', 'dev') ...
场景三:相邻元素分组(如按日期合并日志)
如果日志文件已经按时间排序,用 groupby 可以轻松把同一天的数据放到一起。
from itertools import groupby
logs = [
('2025-01-01', 'login'),
('2025-01-01', 'view_page'),
('2025-01-02', 'logout')
]
for date, entries in groupby(logs, key=lambda x: x[0]):
print(f"{date}: {list(entries)}")
# 2025-01-01: [('2025-01-01', 'login'), ('2025-01-01', 'view_page')]
# 2025-01-02: [('2025-01-02', 'logout')]
场景四:避免多层嵌套循环
要遍历多个列表的所有组合,传统写法是嵌套 for,可读性差且层级深。product 让你的意图一目了然。
# 传统
for x in range(3):
for y in range(2):
print(x, y)
# 使用 product
from itertools import product
for x, y in product(range(3), range(2)):
print(x, y)
性能与内存优势
因为所有函数都返回迭代器,它们可以连接成流水线(pipeline)处理大数据而不产生中间列表。例如:
from itertools import islice, filterfalse
# 从某个无限流或大文件中取前10个非空白行
lines = filterfalse(str.isspace, some_huge_file)
first_10 = islice(lines, 10)
全程没有创建一个巨大的临时集合,内存占用极低。这使得 itertools 成为数据管道、流式处理和算法实现中对“可迭代对象”进行变换的标准工具。
什么时候不用 itertools?
如果数据量很小,用简单的列表推导式或生成器表达式就够,不必强行使用 itertools 增加复杂度。但在处理未知大小或极大的序列、需要组合数学操作、拼接多个迭代器时,它就是标准库中最直接、最高效的选择。
掌握 itertools 后,搭配 functools 和生成器,你可以写出简洁、高效、可读的 Python 数据流代码。这正是在数据处理和算法面试中体现 Python 功力的利器。