Python 把两层列表展开平铺成一层(5种实现方式)

这几天和同事在讨论,如何用 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)]

这个看上去很简洁,不过有类似字符串累加的性能陷阱。后面有性能对比。

reduce 函数

>>> reduce(list.__add__, input)
[(\'A\', 1), (\'B\', 2), (\'C\', 3), (\'D\', 4)]

做序列的累加操作。也是有累加的性能陷阱。

列表推导式

>>> [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)]

通过第三方类库类实现的,相比其他的几个实现,看着还算比较优雅。最后的性能发现居然还很高。

性能大对比

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}
 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容