是否可以为列表理解中的每个项目返回 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))
是否可以为列表理解中的每个项目返回 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))
双重列表理解:
[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]
>>> 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 ... )
注意:正确的方法是使用itertools.chain.from_iterable
或双重列表推导。(它不需要在每个 + 上重新创建列表,因此具有 O(N) 性能而不是 O(N^2) 性能。)sum(..., [])
当我想要一个快速的单线或我赶时间时,我仍然会使用,或者当组合项的数量是有限的(例如<= 10)。这就是为什么我仍然在这里提到它,并提出警告。您还可以使用元组:(((f(x),g(x)) for ...), ()
或根据 khachik 的评论,有一个生成器 fg(x) 产生一个二元组)。
我知道 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
这个 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]
使用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
实现。
善良,悲伤!为什么所有这些 lambdas、flattens、zips 和 sum?这不是最简单和最易读的:
>>> [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)
(用andg(x)
或其他替换最后两个表达式。)