这几天和同事在讨论,如何用 Python 写出优雅的让列表中的列表展开,变成扁平化的列表。
例如
1
2
3
4
5
|
# 期望输入 input = [[( 'A' , 1 ), ( 'B' , 2 )], [( 'C' , 3 ), ( 'D' , 4 )]] # 期望输出 output = [( 'A' , 1 ), ( 'B' , 2 ), ( 'C' , 3 ), ( 'D' , 4 )] |
map 函数合并
1
2
3
|
>>> new = []; map (new.extend, input ); new [ None , None ] [( 'A' , 1 ), ( 'B' , 2 ), ( 'C' , 3 ), ( 'D' , 4 )] |
这个方法看上去还可以,但是有个致命的缺点,就是map函数会返回值,并且这个返回值是没有用的。另外还需要提前声明一个变量,从代码的简洁性上,不够简洁优雅。
sum 函数合并
1
2
|
>>> sum ( input , []) [( 'A' , 1 ), ( 'B' , 2 ), ( 'C' , 3 ), ( 'D' , 4 )] |
这个看上去很简洁,不过有类似字符串累加的性能陷阱。后面有性能对比。
reduce 函数
1
2
|
>>> reduce ( list .__add__, input ) [( 'A' , 1 ), ( 'B' , 2 ), ( 'C' , 3 ), ( 'D' , 4 )] |
做序列的累加操作。也是有累加的性能陷阱。
列表推导式
1
2
|
>>> [item for sublist in input for item in sublist] [( 'A' , 1 ), ( 'B' , 2 ), ( 'C' , 3 ), ( 'D' , 4 )] |
列表推导式,看着有些长,而且还要for循环两次,变成一行理解需要费劲一些,没有那么直观。
itertools 类库
1
2
|
>>> list (itertools.chain( * input )) [( 'A' , 1 ), ( 'B' , 2 ), ( 'C' , 3 ), ( 'D' , 4 )] |
通过第三方类库类实现的,相比其他的几个实现,看着还算比较优雅。最后的性能发现居然还很高。
性能大对比
1
2
3
4
5
6
7
8
9
10
|
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平铺展开
话不多说,直接上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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 |
1
2
3
4
5
6
|
>>> 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 } |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。
原文链接:https://www.chenyudong.com/archives/python-make-flat-list-of-list.html