查看: 397|回复: 3

Python itertools模块常用方法详解与数据处理实战

[复制链接]
发表于 昨天 09:00 | 显示全部楼层 |阅读模式
Python 标准库中的 itertools 模块是处理迭代器的宝藏工具,其核心函数采用惰性求值机制,不一次性加载全部数据,处理超大文件时内存占用极低,且底层用 C 实现,速度远超手写循环。理解 itertools 之前需要区分可迭代对象(Iterable)和迭代器(Iterator):前者能被 for 循环遍历,后者只能调用 next() 向前获取元素,所有 itertools 函数返回的都是迭代器。

一、最常用的函数:日常代码的主力军

chain() 与 chain.from_iterable()
chain() 将多个可迭代对象首尾拼接成一个迭代器,不生成新列表。例如:
  1. from itertools import chain
  2. result = list(chain([1, 2], [3, 4], [5, 6]))
  3. # 输出 [1, 2, 3, 4, 5, 6]
复制代码
chain.from_iterable() 用于展平嵌套列表,避免手动解包:
  1. nested = [[1, 2], [3, 4], [5, 6]]
  2. result = list(chain.from_iterable(nested))
  3. # 输出 [1, 2, 3, 4, 5, 6]
复制代码
实际场景:合并爬虫采集的多个页面数据,比 sum(lists, []) 更高效。

islice() — 迭代器切片
对迭代器执行类似列表的切片操作,但不支持负索引。
  1. from itertools import count, islice
  2. # 从无限计数器中取前5个
  3. first_five = list(islice(count(10), 5))
  4. # 输出 [10, 11, 12, 13, 14]
  5. # 读取大文件的第4到10行,无需载入全部
  6. with open("bigfile.txt") as f:
  7.     lines = list(islice(f, 3, 10))
复制代码

groupby() — 分组(注意预排序)
按照键将连续相同元素分组,类似 Unix 的 uniq 命令。重要:必须先按分组键排序,否则结果异常。
  1. from itertools import groupby
  2. data = [
  3.     {"name": "Alice", "dept": "Engineering"},
  4.     {"name": "Bob", "dept": "Engineering"},
  5.     {"name": "Carol", "dept": "Marketing"},
  6. ]
  7. data.sort(key=lambda x: x["dept"])
  8. for dept, members in groupby(data, key=lambda x: x["dept"]):
  9.     print(dept, list(members))
复制代码

accumulate() — 累积计算
默认做累加,但可接受任意二元函数,实现累积乘积、滚动最大值等。
  1. from itertools import accumulate
  2. import operator
  3. nums = [1, 2, 3, 4, 5]
  4. # 累加:1 3 6 10 15
  5. print(list(accumulate(nums)))
  6. # 累积乘积:1 2 6 24 120
  7. print(list(accumulate(nums, operator.mul)))
  8. # 带初始值
  9. print(list(accumulate(nums, initial=100)))
  10. # 输出 [100, 101, 103, 106, 110, 115]
复制代码

pairwise() 与 batched() — 相邻配对与批量分组
pairwise(Python 3.10+)生成相邻元素配对,便于计算差值:
  1. from itertools import pairwise
  2. prices = [100, 105, 98, 110]
  3. changes = [b - a for a, b in pairwise(prices)]
  4. # 输出 [5, -7, 12]
复制代码
batched(Python 3.12+)将迭代器按固定大小分批,替代手写切片循环:
  1. from itertools import batched
  2. data = range(10)
  3. for batch in batched(data, 3):
  4.     print(batch)
  5. # 输出 (0,1,2) (3,4,5) (6,7,8) (9,)
复制代码

二、进阶函数:用得少但关键时刻救命

starmap() — 解包参数映射
当函数需要接收元组参数时,比 map() 更简洁:
  1. from itertools import starmap
  2. pairs = [(2, 5), (3, 2), (10, 3)]
  3. result = list(starmap(pow, pairs))
  4. # 输出 [32, 9, 1000]
复制代码

tee() — 复制迭代器(注意内存)
将迭代器复制为 n 个独立副本,但内部会缓存进度差,造成内存消耗。使用后原迭代器不应再使用。
  1. from itertools import tee
  2. a, b = tee([1, 2, 3, 4, 5])
  3. print(list(a))  # [1, 2, 3, 4, 5]
  4. print(list(b))  # [1, 2, 3, 4, 5]
复制代码

zip_longest() — 补齐的 zip
以最长序列为准,短序列用 fillvalue 填充,避免丢失数据:
  1. from itertools import zip_longest
  2. a = [1, 2, 3, 4]
  3. b = ['x', 'y']
  4. result = list(zip_longest(a, b, fillvalue='-'))
  5. # 输出 [(1,'x'), (2,'y'), (3,'-'), (4,'-')]
复制代码

takewhile() 与 dropwhile() — 条件截断
takewhile 从开头取满足条件的元素,遇到第一个不满足即停止;dropwhile 跳过开头满足条件的元素,然后输出剩余全部(不再判断)。
  1. from itertools import takewhile, dropwhile
  2. data = [1, 4, 6, 3, 8]
  3. list(takewhile(lambda x: x < 5, data))  # [1, 4]
  4. list(dropwhile(lambda x: x < 5, data))  # [6, 3, 8]
复制代码

三、无限迭代器:谨慎使用,必须配合截断
count(start, step) 无限计数;cycle 无限循环序列;repeat 重复对象。使用时需配合 islice、takewhile 或 zip 截断,否则死循环。
  1. from itertools import count, cycle, repeat
  2. # count 配合 zip 实现自定义起始行号
  3. for i, item in zip(count(1001), ["apple", "banana"]):
  4.     print(i, item)
  5. # 输出 (1001, 'apple') (1002, 'banana')
  6. # repeat 重复固定次数
  7. list(repeat(7, 3))  # [7, 7, 7]
复制代码
注意:cycle 会复制整个序列到内存,处理大序列时需谨慎。

四、组合迭代器:排列组合的数学工具
combinations(组合,顺序无关不重复)、permutations(排列,顺序有关不重复)、product(笛卡尔积)、combinations_with_replacement(允许重复的组合)。
  1. from itertools import combinations, permutations, product, combinations_with_replacement
  2. items = ['A', 'B', 'C']
  3. # 组合:C(3,2)=3
  4. list(combinations(items, 2))  # [('A','B'),('A','C'),('B','C')]
  5. # 排列:P(3,2)=6
  6. list(permutations(items, 2))  # [('A','B'),('A','C'),('B','A'),('B','C'),('C','A'),('C','B')]
  7. # 笛卡尔积
  8. list(product([0,1], repeat=3))  # 8个二进制
  9. # 有放回组合
  10. list(combinations_with_replacement('AB', 2))  # [('A','A'),('A','B'),('B','B')]
复制代码

五、实战:组合使用才是威力
单个函数是积木,组合可搭建高效数据处理流水线。
场景:分页 API 跳过前两页取后三页并展平
  1. from itertools import chain, islice
  2. def fetch_page(n):
  3.     return [n * 10 + i for i in range(5)]
  4. pages = (fetch_page(n) for n in range(10))
  5. result = list(chain.from_iterable(islice(pages, 2, 5)))
  6. # 输出 [20,21,22,23,24, 30,31,32,33,34, 40,41,42,43,44]
复制代码
场景:找出列表中连续递增的片段
  1. from itertools import groupby, pairwise
  2. nums = [1,2,3,1,2,4,5]
  3. diffs = [b - a for a, b in pairwise(nums)]
  4. # 将连续 +1 的差值分组
  5. result = []
  6. for k, g in groupby(enumerate(diffs), lambda x: x[1]):
  7.     if k == 1:
  8.         indices = [i for i,_ in g]
  9.         result.append((indices[0], indices[-1]+2))
  10. # 结果表示从第0到第2、第4到第6为连续递增片段
复制代码

六、注意事项
1. 迭代器只能遍历一次,需要多次使用请转为列表或使用 tee()。
2. groupby 必须预排序,否则分组异常。
3. 无限迭代器必须配合截断函数(islice、takewhile 等)使用,避免死循环。
4. cycle 内部会复制整个序列,内存敏感时需注意。
5. islice 不支持负索引,取最后 N 个元素可用 collections.deque(iterable, maxlen=N)。

掌握 itertools 的关键不是背参数,而是写代码时自然想到用 accumulate、chain.from_iterable 等替代手写循环,写出既优雅又高效的 Python 代码。
回复

使用道具 举报

发表于 昨天 09:10 | 显示全部楼层

Re: Python itertools模块常用方法详解与数据处理实战

非常感谢楼主的详细整理!刚好最近在处理一些大数据流,itertools 的惰性求值确实帮了大忙。你提到的 groupby 预排序强调得很到位,我一开始没注意踩过坑,后来才发现必须先 sort。另外 tee 的内存开销也提醒得很及时,以前总以为复制迭代器没代价。建议新手可以多练习 combine 一下 chain 和 islice,处理超大文件特别顺手。期待你后续的无限迭代器部分!
回复 支持 反对

使用道具 举报

发表于 昨天 09:10 | 显示全部楼层

Re: Python itertools模块常用方法详解与数据处理实战

楼主写得太详细了,把 itertools 的核心用法和内存优势都讲透了,尤其是 groupby 必须预排序这个坑,刚接触时很容易忽略。chain.from_iterable 展平嵌套列表确实比列表推导省心,而 pairwise 配合 accumulate 做差分和累积运算在数据处理里简直神器。想请教一下,实际工作中处理几十 GB 的日志文件时,用 islice 按行切片读取和直接用 enumerate 跳出循环相比,性能差异明显吗?还有 batched 在 3.12 才引入,低版本 Python 下有没有你能推荐的替代写法?
回复 支持 反对

使用道具 举报

发表于 昨天 09:15 | 显示全部楼层

Re: Python itertools模块常用方法详解与数据处理实战

非常感谢楼主这么详尽的分享!平时写脚本时经常用到`chain.from_iterable`展平数据,还有`islice`处理大文件确实比手动切片省内存。之前用`groupby`没注意预排序,踩过坑,楼主特意强调了这点很实用。另外`pairwise`和`batched`在新版本里真的太方便了,之前都是自己写循环分段。收藏了,以后写代码前先翻翻这个贴。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-21 04:54 , Processed in 0.030916 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部