两层嵌套用 itertools.chain.from_iterable 最省事;递归扁平化须加 str/bytes 类型保护;sum(lst, []) 有 O(n²) 性能缺陷;多层不确定时推荐 more-itertools.collapse。

用 itertools.chain.from_iterable 处理两层嵌套最省事
如果嵌套只有两层(比如 [[1, 2], [3, 4], [5]]),itertools.chain.from_iterable 是最快、最直观的选择,它不递归、不建栈、不判断类型,纯粹做“摊开一层”的操作。
常见错误是误用 itertools.chain(*lst):当输入为空列表或含非迭代对象(如 [1, [2, 3]])时会报 TypeError: 'int' object is not iterable;而 from_iterable 只要求外层可迭代,内部元素是否可迭代由你负责——这反而是优势,避免隐式类型转换。
- 适用场景:明确知道结构是“列表的列表”,且内层全是
list或tuple等标准序列 - 性能:比手动
for循环快约 15–20%,C 实现无 Python 解释器开销 - 示例:
from itertools import chain nested = [[1, 2], [3, 4], []] flat = list(chain.from_iterable(nested)) # → [1, 2, 3, 4]
递归扁平化必须加类型保护,否则遇到字符串就崩
Python 的 str 是可迭代对象,纯递归函数(比如 sum(lst, []) 或无检查的 yield from)遇到字符串会把它拆成单字符,把 ['a', ['b', 'c']] 变成 ['a', 'b', 'c'],而不是预期的 ['a', 'b', 'c'](看似一样,但原始字符串被破坏了)。
真正安全的递归写法必须排除 str 和 bytes:
立即学习“Python免费学习笔记(深入)”;
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 判断条件只能用
isinstance(x, (list, tuple, set, frozenset, deque))这类显式白名单,不能用hasattr(x, '__iter__') - 如果数据可能含
dict,要决定是否展开dict.keys()、dict.values()还是跳过——默认应跳过,否则语义混乱 - 示例(带保护):
def flatten(lst): for item in lst: if isinstance(item, (list, tuple)) and not isinstance(item, (str, bytes)): yield from flatten(item) else: yield item <p>list(flatten([1, [2, [3, 4]], 'hello'])) # → [1, 2, 3, 4, 'hello']
sum(lst, []) 看似简洁,但只适合小数据且有严重性能缺陷
这个技巧利用了 list.__add__ 支持中缀 + 的特性,把 sum([[1,2], [3]], []) 展开为 [] + [1,2] + [3]。但它本质是 O(n²):每次 + 都新建列表并拷贝所有已有元素。
实际测试中,对 1000 个长度为 10 的子列表,它比 chain.from_iterable 慢 8 倍以上;超过 5000 项时可能触发内存告警。
- 仅建议用于临时调试、数据量确定小于 100 项的脚本
- 绝对不要在循环里或数据来源不可控时使用(比如解析用户上传的 JSON)
- 遇到空列表会直接报错:
sum([], [])抛TypeError: sum() requires at least one argument,需额外兜底
第三方库选 more-itertools.collapse,不是为了功能,是为了少踩坑
more-itertools 的 collapse 函数做了完整防护:自动跳过字符串、支持自定义忽略类型、可传入 base_type 控制展开深度、还能处理生成器和 map 对象。它不是最快的,但它是目前最接近“拿来即用不出错”的方案。
安装后直接用即可,不用自己反复验证边界情况:
pip install more-itertools- 示例:
from more_itertools import collapse list(collapse([1, [2, [3, 'nope']], {'key': 'value'}])) # → [1, 2, 3, 'nope'] - 注意:它默认展开
dict的值(不是键),如果业务逻辑要求保留dict,得配合base_type=dict参数
多层嵌套的“扁平”没有银弹——两层用 chain.from_iterable,不确定层数又怕出错就上 more-itertools.collapse,自己写递归时第一行必须是类型判断,这三个点漏掉任何一个,后面都可能在某个凌晨三点的生产日志里看到意料之外的字符数组。

















