List comprehension: Returning two (or more) items for each item
Solution 1:
Double list comprehension:
[f(x) for x in range(5) for f in (f1,f2)]
Demo:
>>> 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]
Solution 2:
>>> 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]
Timings:
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
Solution 3:
sum( ([f(x),g(x)] for x in range(n)), [] )
This is equivalent to [f(1),g(1)] + [f(2),g(2)] + [f(3),g(3)] + ...
You can also think of it as:
def flatten(list):
...
flatten( [f(x),g(x)] for x in ... )
note: The right way is to use itertools.chain.from_iterable
or the double list comprehension. (It does not require recreating the list on every +, thus has O(N) performance rather than O(N^2) performance.) I'll still use sum(..., [])
when I want a quick one-liner or I'm in a hurry, or when the number of terms being combined is bounded (e.g. <= 10). That is why I still mention it here, with this caveat. You can also use tuples: ((f(x),g(x)) for ...), ()
(or per khachik's comment, having a generator fg(x) which yields a two-tuple).
Solution 4:
I know OP is looking for a list comprehension solution, but I'd like to offer an alternative using 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)))
which is marginally faster than using double list comprehension.
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 version: 3.8.0