python list comprehension with multiple 'if's
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.
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')]
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]
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.