64

We all know python's

[f(x) for x in y if g(x)]

syntax.

However the AST representation of list comprehension has room for more than one 'if' expression:

comprehension = (expr target, expr iter, expr* ifs)

Can somebody give me an example of python code that would produce an AST with more than one 'if' expression?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Stefan
  • 4,187
  • 1
  • 32
  • 38

5 Answers5

106

Just stack them after one another:

[i for i in range(100) if i > 10 if i < 50]

Produces the integers between 11 and 49, inclusive.

Emil Vikström
  • 90,431
  • 16
  • 141
  • 175
  • 15
    Why is this needed when `and` would achieve exactly the same result? – Will Vousden Mar 06 '13 at 13:14
  • 1
    I have no idea why it is needed, but yes, it behaves as an `and`. Maybe it is not *needed* but merely syntactic sugar. – Emil Vikström Mar 06 '13 at 13:16
  • 22
    In fact, you could even write `if 10 < i < 50`. – tobias_k Mar 06 '13 at 13:16
  • 5
    @EmilVikström Just ran into this question and your comment. Sometimes you do want a nested if (and not a logical "and"). For example if you want to reference a dictionary with a certain key but first you have to make sure the key exists. With a nested if you can first check validity and then avoid an error. – Adi Sarid Jul 26 '17 at 09:27
  • 13
    @AdiSarid but Python have lazy evaluation of the operands around `and`, so you can make your sanity checks on the left hand side of `and` and prevent the right hand side of being evaluated. – Emil Vikström Dec 05 '17 at 10:59
  • Kind of late to the game, but doing the stacked if's can save on processing speed. For example, suppose you have two costly operations that produce `True` or `False` where you want to see `if operation1(a) and operation2(a)`. Writing the `if` statements nested means you only execute operation(b) if operation(a) is true, as opposed to always testing for both conditions. – horcle_buzz Dec 11 '17 at 02:46
  • 9
    @horcle_buzz Read my comment to Adi Sarid above. – Emil Vikström Dec 11 '17 at 06:49
  • 1
    Using `if` instead of `and` may improve readability in the case when the list comprehension is too long to fit on a single line, and lines are split right before each `if` (or `and`). In that case I'd prefer that all condition lines start with an `if`, just for readability. – kadee Nov 23 '20 at 16:06
76

The grammar allows for multiple if statements because you can mix them between the for loops:

[j for i in range(100) if i > 10 for j in range(i) if j < 20]

The comprehension components should be viewed as nested statements, the above translates to:

lst = []
for i in range(100):
    if i > 10:
        for j in range(i):
            if j < 20:
                lst.append(j)

This also means that you can use multiple if statements without for loops in between:

[i for i in range(100) if i > 10 if i < 20]

Although non-sensical (just combine those using and or with chained operators), it does translate to a legal nested set of statements still:

lst = []
for i in range(100):
    if i > 10:
        if i < 20:
            lst.append(i)

The grammar and parser do not specifically disallow such usage, in the same way that Python doesn't disallow you to nest if statements.

Note that PEP 202 – List Comprehensions (the original proposal document that added this feature to the language) actually includes a double-if comprehension in the examples section:

>>> print [(i, f) for i in nums for f in fruit if f[0] == "P" if i%2 == 1]
[(1, 'Peaches'), (1, 'Pears'), (3, 'Peaches'), (3, 'Pears')]
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 4
    Actually, the quoted part of the AST grammar is not neccessary to handle the first part of your answer. That example will create two *comprehension generators* with a single if-statement each. – poke Mar 06 '13 at 13:35
23

Using the built in all() allows you to place multiple Boolean expressions or functions in an iterable and stick in your comprehension. I think it's a pretty under used built in and it keeps readability high.

>>> [x for x in range(20) if all([1 < x < 10, not x & 1])]
[2, 4, 6, 8]

Or

>>> [x for x in range(20) if all([foo(x), bar(x)])]

the any() built in also would work well here if only one condition needed to be satisfied:

>>> [x for x in range(20) if any([1 < x < 10, not x & 1])]
[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18]
kylieCatt
  • 10,672
  • 5
  • 43
  • 51
  • 3
    Note that using all() the conditions will be always evaluated (no short-circuit optimization), plus it needs creating a list for every element of the loop. – Elias Dorneles Oct 28 '17 at 08:27
  • 5
    @elias `all` & `any` *do* short-circuit. But to get full advantage of that you must pass them a generator expression, not a list comprehension. – PM 2Ring Sep 07 '18 at 17:18
8

The language reference gives a better idea about this:

list_comprehension  ::=  expression list_for
list_for            ::=  "for" target_list "in" old_expression_list [list_iter]
list_iter           ::=  list_for | list_if
list_if             ::=  "if" old_expression [list_iter]

As you can see the list comprehension is defined with an optional list_iter at the end—a single list_iter. Now this list_iter can either be another for-part of the list comprehension or an if-condition. The if-condition itself again ends with another optional list_iter. This is essential to make it possible to chain multiple for-parts with optional if-conditions in the same list comprehension. The fact that you could also construct an .. if X if Y if Z part for the list_iter is just a side effect.

So, while the possibility to chain multiple if-conditions alone is not needed, it allows the whole grammar to be defined that way.

poke
  • 369,085
  • 72
  • 557
  • 602
0

Here is an example of using list comprehension with multiple 'if's

code Syntax

 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# List comprehension with multiple 'if' conditions
result = [num for num in numbers if num % 2 == 0 if num > 4]

print(result)
  • Welcome! The question is asking for examples of using multiple if statements in a list comprehension, not for examples of list comprehensions with if statements in general. Can you edit your answer to address multiple if statements? – Kyle Alm May 19 '23 at 09:43
  • 1
    @KyleAlm Thanks. It was misplaced while replying to other answer i apologize. – Jeewan Ghimire May 19 '23 at 12:16
  • @JeewanGhimire How is your answer any different from the already accepted answer from 10 years ago? – NotTheDr01ds Jul 23 '23 at 14:30