我怎样才能从一个列表内涵而不是一个嵌套列表得到一个统一的结果?

我有一个列表 A和一个函数 f,它接受一个 A项并返回一个列表。我可以使用一个列表内涵来转换 A中的所有内容,比如 [f(a) for a in A],但这会返回一个列表列表。假设我的输入是 [a1,a2,a3],结果是 [[b11,b12],[b21,b22],[b31,b32]]

我怎样才能得到 被压扁了列表 [b11,b12,b21,b22,b31,b32]而不是?换句话说,在 Python 中,如何在函数式编程语言中获得传统上称为 flatmap的内容,或者在。NET?

(在实际代码中,A是一个目录列表,而 fos.listdir。我想建立一个子目录的平面列表。)


另请参阅: 如何从列表列表中制作一个平面列表?,了解在创建列表之后将列表展开的更一般的问题。

80077 次浏览

You could try itertools.chain(), like this:

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

itertools.chain() returns an iterator, hence the passing to list().

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

(but Ants's answer is better; +1 for him)

You can find a good answer in the itertools recipes:

import itertools


def flatten(list_of_lists):
return list(itertools.chain.from_iterable(list_of_lists))

Google brought me next solution:

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

You can have nested iterations in a single list comprehension:

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

which is equivalent (at least functionally) to:

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

You could just do the straightforward:

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

You can concatenate lists using the normal addition operator:

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

The built-in function sum will add the numbers in a sequence and can optionally start from a specific value:

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

Combine the above to flatten a list of lists:

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

You can now define your flatmap:

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

Edit: I just saw the critique in the comments for another answer and I guess it is correct that Python will needlessly build and garbage collect lots of smaller lists with this solution. So the best thing that can be said about it is that it is very simple and concise if you're used to functional programming :-)

>>> from functools import reduce  # not needed on Python 2
>>> list_of_lists = [[1, 2],[3, 4, 5], [6]]
>>> reduce(list.__add__, list_of_lists)
[1, 2, 3, 4, 5, 6]

The itertools solution is more efficient, but this feels very pythonic.

import itertools
x=[['b11','b12'],['b21','b22'],['b31']]
y=list(itertools.chain(*x))
print y

itertools will work from python2.3 and greater

The question proposed flatmap. Some implementations are proposed but they may unnecessary creating intermediate lists. Here is one implementation that's based on iterators.

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

In Python 2.x, use itertools.map in place of map.

If listA=[list1,list2,list3]
flattened_list=reduce(lambda x,y:x+y,listA)

This will do.

You can use pyxtension:

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

This is the most simple way to do it:

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

The 'a+b' refers to concatenation of two lists