Is it possible to add a where clause with list comprehension?

balki picture balki · Jul 23, 2012 · Viewed 18.8k times · Source

Consider the following list comprehension

[ (x,f(x)) for x in iterable if f(x) ]

This filters the iterable based a condition f and returns the pairs of x,f(x). The problem with this approach is f(x) is calculated twice. It would be great if we could write like

[ (x,fx) for x in iterable if fx where fx = f(x) ]
or
[ (x,fx) for x in iterable if fx with f(x) as fx ]

But in python we have to write using nested comprehensions to avoid duplicate call to f(x) and it makes the comprehension look less clear

[ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]

Is there any other way to make it more pythonic and readable?


Update

Coming soon in python 3.8! PEP

# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]

Answer

Igor Chubin picture Igor Chubin · Jul 23, 2012

There is no where statement but you can "emulate" it using for:

a=[0]
def f(x):
    a[0] += 1
    return 2*x

print [ (x, y) for x in range(5) for y in [f(x)] if y != 2 ]
print "The function was executed %s times" % a[0]

Execution:

$ python 2.py 
[(0, 0), (2, 4), (3, 6), (4, 8)]
The function was executed 5 times

As you can see, the functions is executed 5 times, not 10 or 9.

This for construction:

for y in [f(x)]

imitate where clause.