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

有没有可能为一个列表内涵中的每个项目返回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))
48051 次浏览
>>> 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

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 ... )

注意: 正确的方法是使用 abc0或双列表内涵。(它不需要在每个 + 上重新创建列表,因此具有 O (N)性能,而不是 O (N ^ 2)性能。)当我想要一个快速的一行程序,或者我很忙,或者组合的术语数量是有界的时候(例如 < = 10) ,我仍然会使用 sum(..., [])。这就是为什么我仍然在这里提到它,带有这个警告。您还可以使用元组: ((f(x),g(x)) for ...), ()(或者根据 khachik 的注释,使用生成器 fg (x)生成两个元组)。

列表内涵:

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

这个 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]

我知道 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

使用 减少的解决方案:

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

这是解决问题的一种实用方法。列表内涵本质上是对数据进行 maping 的另一种方式,但是在这种情况下,输入和输出之间的映射不是一对一的,reduce允许在如何生成输出方面有一定的回旋余地。

一般而言,任何 for实施形式:

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

(例如,对于旨在对列表或类似数据结构 蓄电池模式产生副作用的循环)

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

上帝啊,悲哀! 为什么要写这些兰巴达,平底鞋,拉链和数字? 这难道不是最简单最易读的吗:

>>> [v
...  for x in range(5)
...      for v in (2 * x,
...                2 * x + 1)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>

(将最后两个表达式替换为 f(x)g(x),或者其他什么。)

如果可能的话,你应该选择使用 itertools.product()而不是在一个列表内涵中使用两个 for子句。一些样式指南,如 Google Python 风格指南,禁止在列表内涵中使用多个 for子句。虽然你可能使用的风格指南并没有明确禁止这种做法,但是你仍然可以通过以下方法使理解更加简洁:

from itertools import product


result = [y(x) for y, x in product(range(n), (f, g))]