Python 将嵌套列表展开为扁平列表的 5 种方法及性能对比
本文详细解析 Python 中将两层嵌套列表展开为单层扁平列表的五种实现方式,包括 map、sum、reduce、列表推导式及 itertools.chain。通过实际代码示例与性能测试数据,对比各方法的执行效率与代码简洁性,并补充嵌套字典平铺的递归实现方案,帮助开发者选择最优解。
近期在与技术同事交流时,我们探讨了如何在 Python 中以优雅且高效的方式处理嵌套列表,即将多维列表结构转换为单层扁平列表。
假设我们有一个包含元组的嵌套列表结构,期望将其合并为一个包含所有元素的单一列表。
# 期望输入
input = [[('A', 1), ('B', 2)], [('C', 3), ('D', 4)]]
# 期望输出
output = [('A', 1), ('B', 2), ('C', 3), ('D', 4)]
使用 map 函数进行合并
>>> new = []; map(new.extend, input); new
[None, None]
[('A', 1), ('B', 2), ('C', 3), ('D', 4)]
该方案在视觉上看似可行,但存在显著缺陷:map 函数会返回迭代器对象,其返回值在此场景下无实际用途。此外,该方法要求预先声明一个空列表变量,从代码简洁性和优雅度来看,并非最佳选择。
使用 sum 函数进行合并
>>> sum(input, [])
[('A', 1), ('B', 2), ('C', 3), ('D', 4)]
此写法最为简短直观,但需注意其潜在的性能隐患。类似于字符串累加操作,sum 函数在合并大量列表时可能产生较高的时间复杂度,具体性能表现将在后续对比中展示。
使用 reduce 函数
>>> reduce(list.__add__, input)
[('A', 1), ('B', 2), ('C', 3), ('D', 4)]
reduce 函数用于执行序列的累加操作。与 sum 类似,它同样存在累加过程中的性能陷阱,在处理大规模数据时效率较低。
使用列表推导式
>>> [item for sublist in input for item in sublist]
[('A', 1), ('B', 2), ('C', 3), ('D', 4)]
列表推导式虽然功能强大,但代码长度较长,且涉及双重 for 循环逻辑。将其压缩至单行后,阅读和理解成本增加,直观性不如其他方法。
使用 itertools 类库
>>> list(itertools.chain(*input))
[('A', 1), ('B', 2), ('C', 3), ('D', 4)]
通过引入第三方标准库 itertools 实现,相比前几种原生方法,代码更为简洁优雅。值得注意的是,性能测试结果显示其执行效率极高,是推荐的高效方案之一。
各方法性能大对比
python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(list.__add__,l)'
1000 loops, best of 3: 547 usec per loop
python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 509 usec per loop
python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 52.8 usec per loop
python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99; import itertools;' 'list(itertools.chain(*l))'
10000 loops, best of 3: 35.9 usec per loop
python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'new = []; map(new.extend, l); new'
10000 loops, best of 3: 34.1 usec per loop
欢迎读者就更优雅的代码实现及性能优化策略进行进一步探讨与交流。
补充:Python 将含嵌套结构的字典(dict)平铺展开
直接展示相关实现代码:
def prefix_dict(di_, prefix_s=''):
"""
把字典的每个key都带上前缀prefix_s
:param di_:
:param prefix_s:
:return:
"""
return {prefix_s + k: v for k, v in di_.items()}
def spear_dict(di_, con_s='.'):
"""
展开dict(如果下层还是dict),需要递归,展开到下层的数据类型不是字典为止
可能实用的地方:将文档类的数据格式化成更加关系化的样子可能有用
:param di_: 输入字典
:param con_s: 层级间的连接符号
:return: 深度不大于1的字典,嵌套的其他数据类型照旧
"""
ret_di = {}
for k, v in di_.items():
if type(v) is dict:
v = spear_dict(v)
# 这里或许有不写到这一层的更好写法
# for k_, v_ in v.items():
# ret_di.update({con_s.join([k, k_]): v_})
ret_di.update(prefix_dict(v, prefix_s=k + con_s))
else:
ret_di.update({k: v})
return ret_di
>>> di_
{'title': '新田商业街', 'reliability': 7, 'addressComponents': {'streetNumber': '', 'city': '深圳市', 'street': '', 'province': '广东省', 'district': '龙华区'}, 'location': {'lng': 114.09127044677734, 'lat': 22.700519561767578}, 'adInfo': {'adcode': '440309'}, 'level': 11, 'more_deep': {'loca': {'lng': 114.09127044677734, 'lat': 22.700519561767578}}}
>>> spear_dict(di_)
{'title': '新田商业街', 'reliability': 7, 'addressComponents.streetNumber': '', 'addressComponents.city': '深圳市', 'addressComponents.street': '', 'addressComponents.province': '广东省', 'addressComponents.district': '龙华区', 'location.lng': 114.09127044677734, 'location.lat': 22.700519561767578, 'adInfo.adcode': '440309', 'level': 11, 'more_deep.loca.lng': 114.09127044677734, 'more_deep.loca.lat': 22.700519561767578}
spear_dict(di_, '_')
{'title': '新田商业街', 'reliability': 7, 'addressComponents_streetNumber': '', 'addressComponents_city': '深圳市', 'addressComponents_street': '', 'addressComponents_province': '广东省', 'addressComponents_district': '龙华区', 'location_lng': 114.09127044677734, 'location_lat': 22.700519561767578, 'adInfo_adcode': '440309', 'level': 11, 'more_deep_loca.lng': 114.09127044677734, 'more_deep_loca.lat': 22.700519561767578}
以上为个人经验分享,旨在提供参考。如有疏漏或未尽之处,欢迎指正与赐教。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









