itertools.chain本质是按序拼接多个可迭代对象的惰性迭代器,不创建新列表、不复制数据,仅线性拉通各输入并逐个产出元素。

itertools.chain 本质就是为这种场景设计的:它不求“连续”,只求“按序拼接”——只要每个输入是可迭代对象,无论中间有没有空缺、类型是否混杂、长度是否悬殊,它都能线性拉通。
chain 要求输入是可迭代对象,不是“必须是列表”
很多人卡在第一步:传进去却报 TypeError: 'int' object is not iterable。这不是 chain 的问题,是你传了非可迭代对象。
常见错误现象:
- 误把单个数字、字符串字面量(如
"abc")当“元素”传,其实"abc"是可迭代的,但42不是 - 把已经展开的序列(如
[1,2] + [3,4]结果)再喂给chain,纯属多余
正确做法:
- 确保每个参数本身支持
for x in it:—— 列表、元组、range、map、文件对象、生成器都行 - 混用没问题:
chain([1,2], "ab", range(3))→[1, 2, 'a', 'b', 0, 1, 2] - 如果手头只有单个值(比如一个整数
n),用[n]或(n,)包一层再传
chain(*lists) 和 chain.from_iterable(lists) 到底选哪个?
关键看你的数据结构是“平铺的多个变量”,还是“装在容器里的多个可迭代对象”。
使用场景:
- 你有明确命名的几个列表:
a = [1]; b = [2,3]; c = [4,5,6]→ 用chain(a, b, c) - 你只有一个变量
lists = [[1], [2,3], [4,5,6]]→ 用chain.from_iterable(lists),别写chain(*lists)
为什么不用 *lists?
- 如果
lists有上万个子列表,chain(*lists)会触发 Python 的参数数量限制(RecursionError或SystemError: too many arguments) -
chain.from_iterable不解包,逐个迭代内部项,内存和调用栈都更稳 - 两者返回类型完全一致,行为无差别,只是接口健壮性不同
chain 返回的是单次迭代器,不是 list
这是最常踩的坑:直接打印 chain(...) 看到 ,或尝试 result[0] 报 TypeError: 'itertools.chain' object is not subscriptable。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
原因很直接:chain 不生成新列表,只提供一种“边读边拼”的访问方式。
怎么办?
- 只需要遍历一次(比如喂给
sum()、any()、for循环)→ 直接用,不转list - 需要索引、切片、反复遍历、传给要求
list的函数(如numpy.array())→ 显式转:list(chain(...)) - 若后续要多次用,又不想重复构造
chain,就一次性转成list或tuple存下来
性能提醒:对几百项以内的小数据,list(chain(a,b,c)) 和 a + b + c 差距几乎为零;但前者能混类型,后者要求全是 list。
真正影响效率的不是 chain 本身,而是你怎么用它
很多人以为用了 chain 就自动“高效”,结果发现比 + 还慢——问题往往出在误用模式。
容易被忽略的点:
-
chain省的是内存,不是时间。如果你合并完立刻list(),那和+没本质区别,还多一层封装开销 - 在循环里反复调用
chain(a,b)(比如每次迭代都重建)→ 实际开销远高于一次性构建后复用 - 用
chain.from_iterable时,确保里面每个子项确实是可迭代的。例如chain.from_iterable([1, [2,3]])会报错,因为1不可迭代;得写成chain.from_iterable([[1], [2,3]])
复杂点在于:你得先想清楚——这个合并结果,后面到底怎么用?要随机访问?要流式处理?会不会重用?答案不同,选法就不同。没想清这点,光套 chain 只是换种方式写 bug。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










