itertools.chain(*lst)可快速展平两层嵌套列表,但需确保子项均为可迭代对象;更安全的替代是chain.from_iterable(lst);任意深度嵌套应使用递归生成器,兼顾内存与类型安全。

用 itertools.chain 快速展平两层嵌套列表
两层嵌套(比如 [[1, 2], [3, 4], [5]])是最常见场景,itertools.chain 是最轻量、最直观的解法。它不递归,不深拷贝,性能好,也明确表达“我只是把子列表连起来”这个意图。
常见错误是误用 chain(*lst) 却没处理空子列表或非列表元素,导致 TypeError: 'int' object is not iterable。
- 确保所有子项都是可迭代对象;如果可能含
None或数字,先过滤:[x for x in lst if isinstance(x, (list, tuple))] - 想兼容元组、集合等?直接用
chain.from_iterable(lst),它比chain(*lst)更安全,不依赖解包 - 注意:它只展平一层,
[[[1, 2]], [3]]会变成[[1, 2], 3],不是你想要的最终结果
处理任意深度嵌套时,递归函数比 json.loads 或正则更可靠
有人试过把嵌套列表转成字符串再用正则提取数字,或者用 json.loads(str(lst)) 强转——这两种都踩过坑:前者丢类型(所有数字变字符串),后者在含 tuple、set、自定义对象时直接报 TypeError: Object of type tuple is not JSON serializable。
写个简单递归函数反而更稳:
立即学习“Python免费学习笔记(深入)”;
def flatten(obj):
if isinstance(obj, (list, tuple, set)):
for item in obj:
yield from flatten(item)
else:
yield obj
- 用
yield from避免中间列表,内存友好;要列表就包一层list(flatten(nested)) - 若需保留
str不展开(比如['a', ['b', 'c']]期望输出['a', 'b', 'c']而非['a', 'b', 'c']拆成字符),加判断if isinstance(obj, str): yield obj - 别用
isinstance(obj, collections.abc.Iterable)—— 它把字符串、字节对象也都算进去,容易意外展开
numpy.ndarray.flatten() 和 .ravel() 的区别影响原数组修改
如果你操作的是数值型多维数组(比如图像像素、矩阵运算结果),别用 Python 原生方法,直接上 numpy。但要注意:.flatten() 返回副本,.ravel() 尽量返回视图(view),改它可能改到原数组。
- 需要安全修改?用
arr.flatten()—— 它永远复制,不怕副作用 - 追求性能且确定不改原数据?用
arr.ravel(),但得加order='C'显式指定行优先(默认行为,但显式更稳妥) - 遇到
AttributeError: 'list' object has no attribute 'flatten'?说明你还没转成np.array,先调np.array(nested_list) - 注意:
np.array([[1,2],[3]])会因长度不一致降级为dtype=object,此时.flatten()不会展开内部列表,得先统一结构或换回 Python 递归处理
展平后丢失索引位置?用生成器带坐标更实用
单纯展平常会丢掉“这个值原来在第几层、哪个位置”,比如做调试、映射错误行号、或构建稀疏结构时,你需要知道 7 来自 data[2][0][1]。
这时候别拼字符串路径,用递归生成器返回 (value, path) 元组更灵活:
def flatten_with_path(obj, path=()):
if isinstance(obj, (list, tuple)):
for i, item in enumerate(obj):
yield from flatten_with_path(item, path + (i,))
else:
yield obj, path
- 调用
list(flatten_with_path(nested))得到像[(1, (0, 0)), (2, (0, 1)), (3, (1, 0))]这样的结果 - 路径用元组而非列表,天然可哈希,方便后续做字典键或去重
- 如果嵌套里有字典,且你想展开
dict.values(),得额外加分支:elif isinstance(obj, dict): yield from flatten_with_path(list(obj.values()), path)
展平本身不难,难的是你到底要什么:是快、是安全、是保结构、还是带上下文。选错方法,后面 debug 花的时间远超写那几行代码。










