列表理解:为每个项目返回两个(或更多)项目


88

是否可以为列表理解中的每个项目返回2个(或更多)项目?

我想要的(示例):

[f(x), g(x) for x in range(n)]

应该回来 [f(0), g(0), f(1), g(1), ..., f(n-1), g(n-1)]

因此,可以替换以下代码块:

result = list()
for x in range(n):
    result.add(f(x))
    result.add(g(x))

3
出于好奇,您为什么要这样做?如果不尝试这样做,可能会有更好的方法来实现最终目标。
murgatroid99 2012年

3
主要是因为我喜欢函数式编程。我想将坐标列表映射到屏幕坐标元组以与pyglet.graphics.draw函数一起使用。
Hashmush 2012年

Answers:


52
>>> from itertools import chain
>>> f = lambda x: x + 2
>>> g = lambda x: x ** 2
>>> list(chain.from_iterable((f(x), g(x)) for x in range(3)))
[2, 0, 3, 1, 4, 4]

时间:

from timeit import timeit

f = lambda x: x + 2
g = lambda x: x ** 2

def fg(x):
    yield f(x)
    yield g(x)

print timeit(stmt='list(chain.from_iterable((f(x), g(x)) for x in range(3)))',
             setup='gc.enable(); from itertools import chain; f = lambda x: x + 2; g = lambda x: x ** 2')

print timeit(stmt='list(chain.from_iterable(fg(x) for x in range(3)))',
             setup='gc.enable(); from itertools import chain; from __main__ import fg; f = lambda x: x + 2; g = lambda x: x ** 2')

print timeit(stmt='[func(x) for x in range(3) for func in (f, g)]',
             setup='gc.enable(); f = lambda x: x + 2; g = lambda x: x ** 2')


print timeit(stmt='list(chain.from_iterable((f(x), g(x)) for x in xrange(10**6)))',
             setup='gc.enable(); from itertools import chain; f = lambda x: x + 2; g = lambda x: x ** 2',
             number=20)

print timeit(stmt='list(chain.from_iterable(fg(x) for x in xrange(10**6)))',
             setup='gc.enable(); from itertools import chain; from __main__ import fg; f = lambda x: x + 2; g = lambda x: x ** 2',
             number=20)

print timeit(stmt='[func(x) for x in xrange(10**6) for func in (f, g)]',
             setup='gc.enable(); f = lambda x: x + 2; g = lambda x: x ** 2',
             number=20)

2.69210777094

3.13900787874

1.62461071932

25.5944058287

29.2623711793

25.7211849286


4
此代码创建不必要的元组(f(x), g(x))。最好写成:def fg(x): yield x + 2; yield x ** 2; list(chain.from_iterable(fg(x) for x in range(3)))
卡其奇2012年

1
您甚至可以使用将其概括chain.from_iterable((func(x) for func in funcs) for x in range(n)))。顺便消除了卡其奇的抱怨。(尽管从某种意义上说,我的和他的在过程上本质上是相同的。我们只是简单地定义了内部生成器。)
JAB 2012年

这比我的sum(..., [])回答要好,因为它不需要在每个+上重新创建列表(因此具有O(N)性能,而不是O(N ^ 2)性能)。sum(..., [])当我想要快速的单线或急事时,或者当合并的术语数有界(例如,<= 10)时,我仍然会使用。
ninjagecko 2012年

@khachik我认为这会更快,但是我现在将对这两种方法进行计时,尽管元组在python中生成非常快。
jamylak 2012年

3
第三个答案消失了,看起来像这样: [y for x in range(n) for y in (f(x), g(x))]但这可能会慢一些。@jamylak如果需要,您也可以进行测试。
Hashmush 2012年

117

双清单理解:

[f(x) for x in range(5) for f in (f1,f2)]

演示:

>>> f1 = lambda x: x
>>> f2 = lambda x: 10*x

>>> [f(x) for x in range(5) for f in (f1,f2)]
[0, 0, 1, 10, 2, 20, 3, 30, 4, 40]

10
这很好,因为它表明双重列表组合并不是那么可怕:它们只是嵌套在for循环中,就像for循环一样for x in range(5): for f in (f1, f2): newlist.append(f(x))。我过去常常觉得有些困惑,因为我一直试图颠倒顺序。
DSM

1
这应该是公认的答案,谢谢!
Wingjam

@DSM我认为,这将永远令人困惑。)
Winand

11
sum( ([f(x),g(x)] for x in range(n)), [] )

这相当于 [f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...

您也可以将其视为:

def flatten(list):
    ...

flatten( [f(x),g(x)] for x in ... )

注意:正确的方法是使用itertools.chain.from_iterable或双重列表理解。(它不需要在每个+上重新创建列表,因此具有O(N)的性能,而不是O(N ^ 2)的性能。)sum(..., [])当我需要快速单线或急着时,我仍然会使用,或者合并的术语数有界(例如,<= 10)。这就是为什么在此我要提一下这一警告。您还可以使用元组:(((f(x),g(x)) for ...), ()或根据khachik的评论,使用生成器fg(x)生成两个元组)。


@ArashThr:它在做[f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
ninjagecko 2012年

您能解释一下它到底在做什么吗?
Rsh 2012年

注意:这具有O(N ^ 2)运行时,因此在庞大的列表上可能会很慢。
jamylak 2012年

1
@jamylak:是的,我也在评论中的回答中提到了这一点。=)
ninjagecko 2012年

我认为sum()以这种方式滥用是一种反模式,在任何情况下都没有使用它的任何理由。您其他答案中的代码键入较少,因此即使借口“当我想要快速单线或我急时”也无法真正削减代码。
Sven Marnach

2

此lambda函数将两个列表压缩为一个列表:

zipped = lambda L1, L2: [L[i] 
                         for i in range(min(len(L1), len(L2))) 
                         for L in (L1, L2)]

例:

>>> f = [x for x in range(5)]
>>> g = [x*10 for x in range(5)]
>>> zipped(f, g)
[0, 0, 1, 10, 2, 20, 3, 30, 4, 40]

1

我知道OP正在寻找列表理解解决方案,但我想提供一种使用的替代方法list.extend()

f = lambda x: x
g = lambda x: 10*x

result = []
extend = result.extend
for x in range(5):
    extend((f(x),g(x)))

这比使用双列表理解要快。

nums = range(100000)

def double_comprehension():
    return [func(x) for x in nums for func in (f,g)]

def list_extend():
    result = []
    extend = result.extend
    for x in nums:
        extend((f(x),g(x)))
    return result

%timeit -n100 double_comprehension()
23.4 ms ± 67 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit -n100 list_extend()
20.5 ms ± 213 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Python版本:3.8.0


0

一种使用reduce的解决方案:

from functools import reduce

f    = lambda x: f"f({x})" ## Just for example
g    = lambda x: f"g({x})"
data = [1, 2, 3]

reduce(lambda acc, x: acc + [f(x), g(x)], data, [])
# => ['f(1)', 'g(1)', 'f(2)', 'g(2)', 'f(3)', 'g(3)']

尽管不是列表理解,但这是解决问题的一种实用方法。列表理解本质上是一种map处理数据的方法,但是在这种情况下,输入与输出之间的映射不是一对一的,reduce允许如何生成输出空间。

通常,for以下形式的任何实现:

result = []
for n in some_data:
  result += some_operation()
  ## etc.

(即,用于在列表或类似数据结构上产生副作用的循环)

可以重构为声明式map/reduce/filter实现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.