Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to capture the returned value from a Python list comprehension for use a condition?

I want to construct a value in a list comprehension, but also filter on that value. For example:

[expensive_function(x) for x in generator where expensive_function(x) < 5]

I want to avoid calling expensive_function twice per iteration.

The generator may return an infinite series, and list comprehensions aren't lazily evaluated. So this wouldn't work:

[y in [expensive_function(x) for x in generator where expensive_function(x)] where y < 5]

I could write this another way, but it feels right for a list comprehension and I'm sure this is a common usage pattern (possible or not!).

like image 679
Joe Avatar asked Jan 06 '12 16:01

Joe


People also ask

Can you return a list comprehension Python?

List comprehensions cannot contain statements, only expressions. In fact, that's true for all expressions in Python: they can only contain other expressions. So, no, you can't put a return inside a list comprehension.

Does list comprehension return a list?

List comprehensions are used for creating new lists from other iterables. As list comprehensions return lists, they consist of brackets containing the expression, which is executed for each element along with the for loop to iterate over each element.

What is the advantage of use list comprehension in Python?

One main benefit of using a list comprehension in Python is that it's a single tool that you can use in many different situations. In addition to standard list creation, list comprehensions can also be used for mapping and filtering. You don't have to use a different approach for each scenario.


2 Answers

If generator may be infinite, you do not want to use a list comprehension. And not everything has to be a one-liner.

def filtered_gen(gen):
    for item in gen:
        result = expensive_function(item)
        if result < 5:
            yield result
like image 51
Ethan Furman Avatar answered Sep 22 '22 15:09

Ethan Furman


I'm going to answer the part of the question about how to capture intermediate results in a list comprehension for use in a condition, and ignore the question of a list comprehension built from an infinite generator (which obviously isn't going to work), just in case anyone looking for an answer to the question in the title comes here.

So, you have a list comprehension like this:

[expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0]

And you want to avoid calculating expensive_function twice when it passes your filter. Languages with more expressive comprehension syntax (Scala, Haskell, etc) allow you to simply assign names to expressions calculated from comprehension variables, which lets you do things like the following:

# NOT REAL PYTHON
[result for x in xrange(5) for result = expensive_function(x) if result % 2 == 0]

But you can easily emulate this by turning the assignment result = expensive_function(x) into another for iteration over a sequence of one element:

[result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0]

And the proof:

>>> def expensive_function(x):
        print 'expensive_function({})'.format(x)
        return x + 10
>>> [expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0]
expensive_function(0)
expensive_function(0)
expensive_function(1)
expensive_function(2)
expensive_function(2)
expensive_function(3)
expensive_function(4)
expensive_function(4)
[10, 12, 14]
>>> [result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0]
expensive_function(0)
expensive_function(1)
expensive_function(2)
expensive_function(3)
expensive_function(4)
[10, 12, 14]
like image 33
Ben Avatar answered Sep 24 '22 15:09

Ben