Nyle
Nyle

Reputation: 51

Python unpacking from list comprehension over empty input

When working with a function that returns multiple values with a tuple, I will often find myself using the following idiom to unpack the results from inside a list comprehension.

fiz, buz = zip(*[f(x) for x in input])

Most of the time this works fine, but it throws a ValueError: need more than 0 values to unpack if input is empty. The two ways I can think of to get around this are

fiz = []
buz = []
for x in input:
    a, b = f(x)
    fiz.append(a)
    buz.append(b)

and

if input:
    fiz, buz = zip(*[f(x) for x in input])
else:
    fiz, buz = [], []

but neither of these feels especially Pythonic—the former is overly verbose and the latter doesn't work if input is a generator rather than a list (in addition to requiring an if/else where I feel like one really shouldn't be needed).

Is there a good simple way to do this? I've mostly been working in Python 2.7 recently, but would also be interested in knowing any Python 3 solutions if they are different.

Upvotes: 4

Views: 2315

Answers (3)

woodpav
woodpav

Reputation: 2027

If f = lambda x: (x,x**2) then this works

x,y = zip(*map(f,input)) if len(input) else ((),())

If input=[], x=() and y=().

If input=[2], x=(2,) and y=(4,)

If input=[2,3], x=(2,3) and y=(4,9)

They're tuples (not lists), but thats thats pretty easy to change.

Upvotes: 1

Thierry Lathuille
Thierry Lathuille

Reputation: 24232

You could use:

fiz = []
buz = []
results = [fiz, buz]

for x in input:
    list(map(lambda res, val: res.append(val), results, f(x)))

print(results)

Note about list(map(...)): in Python3, map returns a generator, so we must use it if we want the lambda to be executed.list does it.

(adapted from my answer to Pythonic way to append output of function to several lists, where you could find other ideas.)

Upvotes: 0

Tom Wyllie
Tom Wyllie

Reputation: 2085

I would consider using collections.namedtuple() for this sort of thing. I believe the named tuples are deemed more pythonic, and should avoid the need for complicated list comprehensions and zipping / unpacking.


From the documentation:

>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)

Upvotes: 0

Related Questions