Justin Justin - 1 month ago 6
Python Question

Handling empty case with tuple filtering and unpacking

I have a situation with some parallel lists that need to be filtered based on the values in one of the lists. Sometimes I write something like this to filter them:

lista = [1, 2, 3]
listb = [7, 8, 9]
filtered_a, filtered_b = zip(*[(a, b) for (a, b) in zip(lista, listb) if a < 3])


This gives
filtered_a == (1, 2)
and
filtered_b == (7, 8)


However, changing the final condition from
a < 3
to
a < 0
causes an exception to be raised:

Traceback (most recent call last):
...
ValueError: need more than 0 values to unpack


I know why this is happening: the list comprehension is empty, so it's like calling
zip(*[])
, which is like
zip()
, which just returns an empty list which cannot be unpacked into separate filtered_a and filtered_b iterables.

Is there a better (shorter, simpler, more pythonic) filtering function that handles the empty case? In the empty case, I would expect filtered_a, and filtered_b to be empty iterables so any following code could remain unchanged.

Answer

You could simply short-circuit with the default values:

filtered_a, filtered_b = zip(*[(a, b) for a, b in zip(lista, listb) if a < 0]) or ([], [])
print(filtered_b, filtered_a)
# [] []

For Python 3, you'll need to call list on the iterator returned by zip so the first operand can be evaluated as an empty list (not an iterator), else the default value is never reached even when the iterator is potentially empty since bool(iter([])) is True.