Handling empty case with tuple filtering and unpacking

浪尽此生 提交于 2021-02-20 09:31:43


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.


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.


I'd probably do something like:

lista = [1, 2, 3]
listb = [7, 8, 9]

filtered_abs = ((a, b) for (a, b) in zip(lista, listb) if a < 3])

for a, b in filtered_abs:
    do_thing(a, b)