python list comprehensions; 压缩列表列表?

Ste*_*per 67 python functional-programming list-comprehension

家伙.我正试图找到一个问题最优雅的解决方案,并想知道python是否内置了我正在尝试做的事情.

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

[f(a) for a in A]
Run Code Online (Sandbox Code Playgroud)

但这会返回列表清单;

[a1,a2,a3] => [[b11,b12],[b21,b22],[b31,b32]]
Run Code Online (Sandbox Code Playgroud)

我真正想要的是得到扁平化的清单;

[b11,b12,b21,b22,b31,b32]
Run Code Online (Sandbox Code Playgroud)

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

我试图解决的实际问题是这个; 从目录列表开始,找到所有子目录.所以;

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

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

Ant*_*sma 99

您可以在单个列表解析中嵌套迭代:

[filename for path in dirs for filename in os.listdir(path)]
Run Code Online (Sandbox Code Playgroud)

  • 虽然聪明,但很难理解,也不是很易读. (51认同)
  • Dave31415:`[list for listoflists for list in list in list] (16认同)
  • "可读性"是一种主观判断.我觉得这个解决方案很可读. (8认同)
  • 我认为它也是可读的,直到我看到术语的顺序... :( (4认同)
  • 并没有真正按照要求回答问题。这是一种解决方法,可以避免最初遇到此问题。如果您已经有一个列表列表,该怎么办。例如,如果您的列表列表是多处理模块的map函数的结果,该怎么办?也许itertools解决方案或reduce解决方案是最好的。 (2认同)

Jul*_*ian 66

>>> listOfLists = [[1, 2],[3, 4, 5], [6]]
>>> reduce(list.__add__, listOfLists)
[1, 2, 3, 4, 5, 6]
Run Code Online (Sandbox Code Playgroud)

我猜测itertools解决方案比这更有效,但这感觉非常pythonic并且避免了为了单个列表操作而必须导入库.

  • 这绝对是最好的解决方案. (5认同)
  • 对于 1,000 个子列表的列表,这[慢 100 倍](/sf/ask/66704011/ /45323085#45323085) 随着列表的增长,“itertools”方式和差异只会变得更糟。 (5认同)

rob*_*rob 50

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

def flatten(listOfLists):
    return list(chain.from_iterable(listOfLists))
Run Code Online (Sandbox Code Playgroud)

(注意:需要Python 2.6+)


Wai*_*ung 24

提出的问题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']
Run Code Online (Sandbox Code Playgroud)

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


Ano*_*non 16

你可以直截了当地说:

subs = []
for d in dirs:
    subs.extend(os.listdir(d))
Run Code Online (Sandbox Code Playgroud)


Mar*_*ler 11

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

>>> [1, 2] + [3, 4]
[1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

内置函数sum将按顺序添加数字,并且可以选择从特定值开始:

>>> sum(xrange(10), 100)
145
Run Code Online (Sandbox Code Playgroud)

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

>>> sum([[1, 2], [3, 4]], [])
[1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

您现在可以定义flatmap:

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

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


Dar*_*ght 11

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

itertools将从python2.3及更高版本开始工作


Ric*_*dle 7

subs = []
map(subs.extend, (os.listdir(d) for d in dirs))
Run Code Online (Sandbox Code Playgroud)

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


Ste*_*eef 5

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

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

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