91

伙计们。我试图找到一个问题的最优雅的解决方案,并想知道 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 给了我一个列表,但我真的想要一个列表。

4

14 回答 14

133

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

[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)
于 2009-07-02T23:32:56.933 回答
88
>>> from functools import reduce
>>> listOfLists = [[1, 2],[3, 4, 5], [6]]
>>> reduce(list.__add__, listOfLists)
[1, 2, 3, 4, 5, 6]

我猜 itertools 解决方案比这更有效,但这感觉非常pythonic。

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

于 2010-01-17T18:32:30.687 回答
62

您可以在itertools 的食谱中找到一个很好的答案:

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

(注:需要 Python 2.6+)

于 2009-07-02T22:50:47.487 回答
32

提出的问题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

于 2013-11-17T23:07:11.797 回答
19

你可以直接做:

subs = []
for d in dirs:
    subs.extend(os.listdir(d))
于 2009-07-02T23:37:11.713 回答
17

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

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

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

于 2009-07-03T12:47:57.377 回答
11
import itertools
x=[['b11','b12'],['b21','b22'],['b31']]
y=list(itertools.chain(*x))
print y

itertools 将在 python2.3 及更高版本中工作

于 2012-11-21T16:48:02.583 回答
9
subs = []
map(subs.extend, (os.listdir(d) for d in dirs))

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

于 2009-07-02T22:48:38.647 回答
4

你可以试试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().

于 2009-07-02T22:47:52.797 回答
4

这是最简单的方法:

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

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

于 2021-01-12T17:37:39.240 回答
2

谷歌给我带来了下一个解决方案:

def flatten(l):
   if isinstance(l,list):
      return sum(map(flatten,l))
   else:
      return l
于 2009-07-02T22:52:51.990 回答
1
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
于 2017-07-05T14:09:58.350 回答
1

您可以使用pyxtension

from pyxtension.streams import stream
stream([ [1,2,3], [4,5], [], [6] ]).flatMap() == range(7)
于 2017-11-03T22:41:17.850 回答
0
If listA=[list1,list2,list3]
flattened_list=reduce(lambda x,y:x+y,listA)

这会做。

于 2015-07-18T08:39:41.650 回答