1

I have a collection of functions fns and each function returns a pair of integers [a,b]. I need to call each function and join the result in one flat list. Without comprehensions, the code would look like this:

res = []
for fn in fns:
    res += fn()
return res

The question is, can I write this as a oneliner? I had high hopes with [*fn() for fn in fns], however Python unfortunately does not understand the unpack operator here and throws a syntax error.

karlosss
  • 2,816
  • 7
  • 26
  • 42
  • Does this answer your question? [Unpacking tuples in a python list comprehension (cannot use the \*-operator)](https://stackoverflow.com/questions/37279653/unpacking-tuples-in-a-python-list-comprehension-cannot-use-the-operator) – zr0gravity7 Jul 23 '21 at 20:40
  • Unfortunately, not. The answer says to change the way the tuples are created, which is not possible here - I receive them as the inputs. – karlosss Jul 23 '21 at 20:45

2 Answers2

3

Simply use a double comprehension to unpack your values:

res = [i for fn in fns for i in fn()]

# Same as:
res = []
for fn in fns:
    for i in fn():
        res += i

or use itertools.chain.from_iterable:

import itertools

res = list(itertools.chain.from_iterable(fn() for fn in fns))

Demo:

def a():
    return (1, 2)

def b():
    return (3, 4)

def c():
    return (5, 6)

fns = [a, b, c]
res = list(itertools.chain.from_iterable(fn() for fn in fns))
>>> res
[1, 2, 3, 4, 5, 6]
Corralien
  • 109,409
  • 8
  • 28
  • 52
  • I tried the "simple" variant before, however I had the `for`s vice versa, `[i for i in fn() for fn in fns]`, which complains that `fn` is not defined...is it simple enough to explain why you need to swap them, which I find very counter-intuitive? – karlosss Jul 23 '21 at 21:01
  • Thank you for the edit, apparently I understood multiple comprehension wrong from the very beginning. – karlosss Jul 23 '21 at 21:08
  • 1
    You should read this (I know it's counterintuitive): https://stackoverflow.com/a/20639246/15239951 – Corralien Jul 23 '21 at 21:10
1

A not so elegant way to do it, but still an one-liner, would be to use sum():

result = sum([fn() for fn in fns], [])

As explained here, sum can take two parameters, the iterable and the start.

So, if you want to sum a list, for example l = [1, 2, 3, 4], you call:

r = sum(l)

which is basically:

r = sum(l, 0)

where 0 is the start number to be added in sum.

The above is the shortcut for:

r = 0
for elem in l:
    r += elem

The same way works for lists. The one-liner in the beginning is the shortcut for your code. The empty list [] needs to be set, because otherwise sum will default that argument to 0, which will throw an error:

TypeError: unsupported operand type(s) for +: 'int' and 'list'

and that is because you cannot add a list to a number.

Vasilis G.
  • 7,556
  • 4
  • 19
  • 29
  • I tried `sum`, but without the second argument, and even when I see it now, I don't understand it...can you please explain why you need it? – karlosss Jul 23 '21 at 20:55
  • 1
    Perfect, with the explanation it does not look that much inelegant actually. Though I still like the double comprehension `[i for fn in fns for i in fn()]` a bit more. – karlosss Jul 23 '21 at 21:10