I was wondering if someone has a nice clean Pythonic and effective technique for implementing comprehensions that involve the same expression in the guard as in the result. To be clear, consider the following simple example:
def f(a):
print "Calling", a
return a + 1
print [ f(v) for v in xrange(3) if f(v) > 1 ]
This will print
Calling 0
Calling 1
Calling 1
Calling 2
Calling 2
[2, 3]
proving that f is called twice for most elements. This is fine and what we want if f has side-effects, but if f is some expensive operation without side-effects, the duplicate call is not desirable. But the solution that only calls f once for each element seems clumsy/verbose to me:
intermediate = ( f(v) for v in xrange(3) )
print [ r for r in intermediate if r > 1 ]
even if it is contracted into one line
print [ r for r in ( f(v) for v in xrange(3) ) if r > 1 ]
So, can anyone come up with something better?
You can use the filter()
function:
filter(lambda x: x > 1, [ f(v) for v in xrange(3)])
But that's about as verbose as your last suggested solution.
How about memoizing f
, e.g.:?
def f(...): ...
def some_or_other():
f = functools.lru_cache(1)(f)
[ f(v) for v in xrange(3) if f(v) > 1 ]
Memoizing locally, withing the scope of your call site has the advantage that once some_or_other()
returns, "memo" memory will be garbage collected, and you don't have to worry about references to v
that were passed to f()
.
Since it's local, memo size limit 1 is sufficient.
In the simple case, you can also memoize f
globally:
@functools.lru_cache()
def f(...): ...
[ f(v) for v in xrange(3) if f(v) > 1 ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With