【问题标题】:python list comprehensions; compressing a list of lists?python列表推导;压缩列表列表?
【发布时间】:2010-11-07 19:13:04
【问题描述】:

伙计们。我试图找到一个问题的最优雅的解决方案,并想知道 python 是否有任何内置的东西来做我想做的事情。

我正在做的是这个。我有一个列表A,还有一个函数f,它接受一个项目并返回一个列表。我可以使用列表理解来转换 A 中的所有内容,就像这样;

[f(a) for a in A]

但这会返回一个列表列表;

[a1,a2,a3] => [[b11,b12],[b21,b22],[b31,b32]]

我真正想要的是得到扁平化的列表;

[b11,b12,b21,b22,b31,b32]

现在,其他语言都有它;它在函数式编程语言中传统上称为flatmap,而.Net 将其称为SelectMany。 python有类似的东西吗?有没有一种巧妙的方法可以将函数映射到列表并展平结果?

我要解决的实际问题是这样的;从目录列表开始,找到所有子目录。所以;

import os
dirs = ["c:\\usr", "c:\\temp"]
subs = [os.listdir(d) for d in dirs]
print subs

currentliy 给了我一个列表列表,但我真的想要一个列表。

【问题讨论】:

标签: functional-programming python list-comprehension


【解决方案1】:

这是最简单的方法:

def flatMap(array):
  return reduce(lambda a,b: a+b, array) 

'a+b' 是指两个列表的串联

【讨论】:

    【解决方案2】:

    您可以在单个列表推导中进行嵌套迭代:

    [filename for path in dirs for filename in os.listdir(path)]
    

    相当于(至少在功能上):

    filenames = []
    for path in dirs:
        for filename in os.listdir(path):
            filenames.append(filename)
    

    【讨论】:

    • 虽然聪明,但很难理解,可读性也不强。
    • 并没有真正回答所问的问题。这是一开始就没有遇到问题的解决方法。如果您已经有一个列表列表怎么办。例如,如果您的列表列表是多处理模块的 map 函数的结果怎么办?也许 itertools 解决方案或 reduce 解决方案是最好的。
    • Dave31415: [ item for list in listoflists for item in list ]
    • “可读性”是主观判断。我觉得这个解决方案非常易读。
    • 我也认为它是可读的,直到我看到术语的顺序...... :(
    【解决方案3】:
    >>> from functools import reduce
    >>> listOfLists = [[1, 2],[3, 4, 5], [6]]
    >>> reduce(list.__add__, listOfLists)
    [1, 2, 3, 4, 5, 6]
    

    我猜 itertools 解决方案比这更有效,但这感觉很 Python。

    在 Python 2 中,它避免了仅仅为了单个列表操作而必须导入库(因为 reduce 是内置的)。

    【讨论】:

    • 这绝对是最好的解决方案。
    • 我应该导入什么才能调用reduce,是pandasscipy 还是functools
    • 在 Python 2 中,它是内置的。对于 Python3,functools 版本是一样的。
    • 该死的!那是天才
    【解决方案4】:

    提出的问题flatmap。提出了一些实现,但它们可能不需要创建中间列表。这是一种基于迭代器的实现。

    def flatmap(func, *iterable):
        return itertools.chain.from_iterable(map(func, *iterable))
    
    In [148]: list(flatmap(os.listdir, ['c:/mfg','c:/Intel']))
    Out[148]: ['SPEC.pdf', 'W7ADD64EN006.cdr', 'W7ADD64EN006.pdf', 'ExtremeGraphics', 'Logs']
    

    在 Python 2.x 中,使用 itertools.map 代替 map

    【讨论】:

      【解决方案5】:

      你可以使用pyxtension:

      from pyxtension.streams import stream
      stream([ [1,2,3], [4,5], [], [6] ]).flatMap() == range(7)
      

      【讨论】:

        【解决方案6】:
        def flat_list(arr):
            send_back = []
            for i in arr:
                if type(i) == list:
                    send_back += flat_list(i)
                else:
                    send_back.append(i)
            return send_back
        

        【讨论】:

          【解决方案7】:
          If listA=[list1,list2,list3]
          flattened_list=reduce(lambda x,y:x+y,listA)
          

          这样就可以了。

          【讨论】:

          • 如果子列表很大,这是一个非常低效的解决方案。两个列表之间的+ 运算符为 O(n+m)
          【解决方案8】:
          import itertools
          x=[['b11','b12'],['b21','b22'],['b31']]
          y=list(itertools.chain(*x))
          print y
          

          itertools 适用于 python2.3 及更高版本

          【讨论】:

            【解决方案9】:

            您可以使用普通的加法运算符连接列表:

            >>> [1, 2] + [3, 4]
            [1, 2, 3, 4]
            

            内置函数sum 将添加一个序列中的数字,并且可以选择从特定值开始:

            >>> sum(xrange(10), 100)
            145
            

            结合以上内容以展平列表列表:

            >>> sum([[1, 2], [3, 4]], [])
            [1, 2, 3, 4]
            

            您现在可以定义您的flatmap

            >>> def flatmap(f, seq):
            ...   return sum([f(s) for s in seq], [])
            ... 
            >>> flatmap(range, [1,2,3])
            [0, 0, 1, 0, 1, 2]
            

            编辑:我刚刚在 cmets 中看到了对 another answer 的批评,我猜 Python 将使用此解决方案不必要地构建和垃圾收集许多较小的列表是正确的。因此,可以说的最好的一点是,如果您习惯于函数式编程,它非常简单明了:-)

            【讨论】:

              【解决方案10】:
              subs = []
              map(subs.extend, (os.listdir(d) for d in dirs))
              

              (但蚂蚁的回答更好;+1 为他)

              【讨论】:

              • 使用 reduce(或 sum,它可以为您节省许多字符和导入;-)因为这是错误的——您一直在无用地丢弃旧列表来为每个 d 创建一个新列表。 @Ants 有正确的答案(@Steve 聪明地接受它!)。
              • 你不能说这是一个糟糕的解决方案。这取决于性能是否是一个问题。除非有优化的理由,否则简单会更好。这就是为什么 reduce 方法最适合许多问题的原因。例如,您有一个慢速函数,它生成一个包含几百个对象的列表。您想通过使用多处理“地图”功能来加快速度。因此,您创建 4 个进程并使用 reduce 对它们进行平面映射。在这种情况下,reduce 函数很好并且非常易读。也就是说,您最好指出为什么这可能不是最理想的。但它并不总是次优的。
              【解决方案11】:

              你可以直接做:

              subs = []
              for d in dirs:
                  subs.extend(os.listdir(d))
              

              【讨论】:

              • 是的,这很好(虽然不如@Ants'),所以我给它一个 +1 以纪念它的简单性!
              【解决方案12】:

              Google 为我带来了下一个解决方案:

              def flatten(l):
                 if isinstance(l,list):
                    return sum(map(flatten,l))
                 else:
                    return l
              

              【讨论】:

              • 如果它也处理生成器表达式会更好,如果你解释如何使用它会更好......
              【解决方案13】:

              你可以在itertools' recipes:找到一个好的答案

              def flatten(listOfLists):
                  return list(chain.from_iterable(listOfLists))
              

              (注意:需要 Python 2.6+)

              【讨论】:

              【解决方案14】:

              你可以试试itertools.chain(),像这样:

              import itertools
              import os
              dirs = ["c:\\usr", "c:\\temp"]
              subs = list(itertools.chain(*[os.listdir(d) for d in dirs]))
              print subs
              

              itertools.chain() 返回一个迭代器,因此传递给list()

              【讨论】:

                猜你喜欢
                • 2013-11-09
                • 2018-06-03
                • 2011-11-20
                • 2015-09-21
                • 1970-01-01
                • 1970-01-01
                • 2014-10-06
                • 1970-01-01
                • 2016-02-13
                相关资源
                最近更新 更多