itertools.chain 是连接多个生成器最轻量的方式,惰性求值、不占内存;需传已调用的可迭代对象,不可传未调用函数或标量;支持 chain(*gens) 和 chain.from_iterable(gens),后者更健壮;返回单次迭代器,不可索引、不可复用。

itertools.chain 是连接多个生成器最轻量、最直接的方式——它不消耗内存建中间列表,也不触发生成器提前执行,只在迭代时按需拉取每个生成器的元素。
传参必须是“可迭代对象”,不是“单个值”
常见错误是把生成器函数名(没调用)或单个标量当参数传:chain(gen) 没问题,但 chain(gen()) 才对;而 chain(42) 会报 TypeError: 'int' object is not iterable。
- ✅ 正确:传已调用的生成器对象,如
chain(gen_a(), gen_b(), range(3)) - ✅ 字符串、元组、map 对象也合法:
chain("ab", (1, 2), map(int, ["3"])) - ❌ 错误:传未调用的函数
chain(gen_a, gen_b)→ 得到的是函数对象,不可迭代 - ❌ 错误:传裸数字或
None:chain(1, [2, 3])→ 第一个参数就失败
chain(*generators) vs chain.from_iterable(generators_list)
区别不在功能,而在数据组织方式和健壮性:
- 用
chain(*generators)当你有明确变量:a = gen1(); b = gen2(); c = gen3(); combined = chain(a, b, c) - 用
chain.from_iterable(generators_list)当你只有一个容器变量:gens = [gen1(), gen2(), gen3()]; combined = chain.from_iterable(gens) - 如果
gens长度超千,chain(*gens)可能触发SystemError: too many arguments;from_iterable完全避开此限制 - 两者返回类型一致,都是单次使用的迭代器,不能重复遍历
生成器被连接后,仍保持惰性求值
chain 不会立刻执行任何生成器体内的代码,只在第一次 next() 或 for 循环首次取值时才启动第一个生成器。后续生成器也严格按需激活。
- 这意味着你可以安全地连接含耗时 I/O 或条件逻辑的生成器,比如
chain(fetch_logs(), filter_errors(), parse_lines()) - 但如果某个生成器内部抛异常(如网络超时),异常会在实际迭代到它那部分时才暴露,不是在
chain(...)调用时 - 无法对
chain结果做切片或索引:combined[5]报TypeError: 'itertools.chain' object is not subscriptable;需先转list()或用itertools.islice
别忘了它只做线性拼接,不处理嵌套或类型混杂
chain 的设计目标非常明确:按序吐出所有输入的元素。它不会递归展开嵌套,也不会跳过非迭代项。
- 如果你有
[[1, 2], [3, 4]],想扁平化一层 → 用chain.from_iterable(nested) - 如果你有
[1, [2, 3], 4]这种混合结构 →chain直接在1处就报错,必须预处理,例如:chain.from_iterable(x if hasattr(x, '__iter__') and not isinstance(x, (str, bytes)) else [x] for x in mixed) - 性能上,
chain是 C 实现,比等效的手写生成器快 20–30%,尤其在大量小生成器串联时优势明显
chain 返回的迭代器只能消费一次。调试时习惯性 print(list(combined)) 再接着 for 循环,结果第二遍什么也不输出——这不是 bug,是它的设计契约。需要复用时,要么重新构造 chain,要么缓存为 list 或 tuple。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











