Python 列表扁平化:六种方法与选择指南

2026-05-11 31 预计阅读时间:1 分钟
来源:realpython.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:6 分钟

嵌套列表在 Python 开发中随处可见——CSV 读取、JSON 解析、API 返回的分组数据,都可能产生 [[1,2],[3,4],[5]] 这样的结构。当你需要把多层嵌套拍平成一维列表时,Python 提供了从朴素循环到库函数的多种路径。选哪一种,取决于数据规模、嵌套深度和你的可读性偏好。

朴素循环:最稳的基础写法

nested = [[1, 2, 3], [4, 5], [6]]

flat = []
for sublist in nested:
    for item in sublist:
        flat.append(item)

print(flat)  # [1, 2, 3, 4, 5, 6]

两层 for 拼接,逻辑一目了然。对于小数据量或一次性脚本,这完全够用。缺点是三行代码才能完成一件事,且 append 在超长列表上不如预分配高效——不过大多数场景下这点开销可以忽略。

列表推导式:循环的紧凑版

nested = [[1, 2, 3], [4, 5], [6]]

flat = [item for sublist in nested for item in sublist]

print(flat)  # [1, 2, 3, 4, 5, 6]

推导式把上面的双层循环压缩成一行。阅读顺序和循环一致:外层 for 在前,内层 for 在后。这是日常开发中最常见的写法——短、快、不需要额外导入。但嵌套超过两层时推导式会变得难读,该换方法了。

itertools.chain:处理大量子列表的利器

from itertools import chain

nested = [[1, 2, 3], [4, 5], [6]]

flat = list(chain.from_iterable(nested))

print(flat)  # [1, 2, 3, 4, 5, 6]

chain.from_iterable 接收一个可迭代对象,逐个拉出子列表中的元素再串联。它不会先构建中间列表,内存占用更友好。当子列表数量成千上万时,这比推导式更省内存。

注意:chain(*nested) 也能工作,但 * 解包会把所有子列表同时展开为参数,子列表过多时反而失去内存优势。优先用 from_iterable

functools.reduce:函数式风格的单行方案

from functools import reduce

nested = [[1, 2, 3], [4, 5], [6]]

flat = reduce(lambda x, y: x + y, nested)

print(flat)  # [1, 2, 3, 4, 5, 6]

reduce 逐次把两个子列表拼接起来。写法简洁,但每次 + 都生成一个新列表,时间复杂度为 O(n²)。子列表少时无所谓,数据量大时性能会明显劣于 chain。如果偏爱函数式,可以改用 operator.add 替代 lambda,稍微提升可读性:

from functools import reduce
from operator import add

flat = reduce(add, nested)

NumPy:数值数据的专用通道

import numpy as np

nested = [[1, 2, 3], [4, 5], [6]]

arr = np.array(nested)
flat = arr.flatten()

print(flat)  # array([1, 2, 3, 4, 5, 6])

如果你的数据本来就是数值型、且已经在用 NumPy 做计算,flatten()ravel() 是最自然的选择。flatten() 返回副本,ravel() 尽量返回视图——修改视图会影响原数组,需留意。

不适合的场景:纯字符串列表、混合类型列表,或者项目里根本没有 NumPy。为了一次扁平化引入整个 NumPy,得不偿失。

递归:对付不规则深度嵌套

前面所有方法都假设嵌套深度固定为两层。遇到 [[1, [2, 3]], [4, [[5]]]] 这种不规则深度,递归才是正解:

def flatten_recursive(lst):
    result = []
    for item in lst:
        if isinstance(item, list):
            result.extend(flatten_recursive(item))
        else:
            result.append(item)
    return result

nested = [[1, [2, 3]], [4, [[5]]]]

print(flatten_recursive(nested))  # [1, 2, 3, 4, 5]

递归逐层判断:是列表就继续深入,不是列表就收集。这能处理任意深度,但有两个风险——极深嵌套会触发 Python 的递归深度限制(默认 1000),且 isinstance(item, list) 只认 list,不认 tuple 等其他序列类型。生产环境中可改为 collections.abc.Iterable 判断,但要排除字符串(字符串也是 Iterable):

from collections.abc import Iterable

def flatten_deep(lst):
    result = []
    for item in lst:
        if isinstance(item, Iterable) and not isinstance(item, (str, bytes)):
            result.extend(flatten_deep(item))
        else:
            result.append(item)
    return result

方法选择速查

场景 推荐方法 原因
两层嵌套,数据量小 列表推导式 一行搞定,无需导入
两层嵌套,子列表很多 itertools.chain.from_iterable 内存友好,不构建中间列表
已在用 NumPy 的数值数据 arr.flatten() / arr.ravel() 与数组生态一致
不规则深度嵌套 递归函数 其他方法无法处理
偏函数式、数据量小 functools.reduce 简洁但性能差,慎用于大数据

实际项目中最常用的组合是:推导式处理简单场景,chain 处理大量数据,递归处理深度嵌套。其余方法在特定条件下有价值,但不必刻意追求"一行搞定"而牺牲性能或可读性。


相关推荐