Capture method in if statement in list comprehension
I have the following use case:
[(x, f(x)) for x in list_x if f(x) == cond(x)]
Above the list I guess is making the call to f (x) twice? How to avoid this and fix the value of f (x) so that f (x) is called only once.
I guess the simple fix is to convert the above list view to a for loop, but curious if this can be done efficiently using a list comprehension.
source to share
You can use a nested generator expression to call a function just one time:
[(x, fx) for (x, fx) in ((x, f(x)) for x in list_x) if fx == cond(x)]
The generator expression is repeated in lockstep to create (x, fx)
list comprehension tuples .
If you find it easier for the reader, you can first split the generator expression into a separate name:
mapped_x = ((x, f(x)) for x in list_x)
filtered_x = [(x, fx) for (x, fx) in mapped_x if fx == cond(x)]
Iterate over this point: the generator expression is executed lazily; the loop for
in the expression advances step by step for each step in the loop for ... in mapped_x
.
Demo:
>>> list_x = range(5)
>>> f = lambda x: print('f({!r})'.format(x)) or (x ** 2 - 1)
>>> cond = lambda x: print('cond({!r})'.format(x)) or x % 2 == 0
>>> mapped_x = ((x, f(x)) for x in list_x)
>>> [(x, fx) for (x, fx) in mapped_x if fx == cond(x)]
f(0)
cond(0)
f(1)
cond(1)
f(2)
cond(2)
f(3)
cond(3)
f(4)
cond(4)
[(1, 0)]
Note that it f(x)
is called only once and the condition is checked immediately.
How effective this is depends on how expensive the call is f(x)
; the generator expression is executed as a separate function block and the interpreter will switch between the two blocks (the loop for comprehension of the list is also a block object).
If f(x)
is a Python function, you already won because you now halve the number of function frame objects you f(x)
create (each call creates a frame object too, and creating that data is relatively expensive). For C functions, you should create some trial runs with timeit
module to see which is faster for your expected list size.
source to share