Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compound conditions in a for loop

Python allows an "if" condition in list comprehensions, e.g.:

[l for l in lines if l.startswith('example')]

This feature is missing in regular "for" loop, so in absence of:

for line in lines if line.startswith('example'):
    statements

one needs to assess the condition in the loop:

for line in lines:
    if line.startswith('example'):
        statements

or to embed the generator expression, like:

for line in [l for l in lines if l.startswith('example')]:
    statements

Is my understanding correct? Is there a better or more pythonic way than ones I listed above to achieve the same result of adding a condition in the for loop?

Please notice "lines" was chosen just as an example, any collection or generator could be there.

like image 381
Pintun Avatar asked Oct 18 '22 22:10

Pintun


2 Answers

Several nice ideas came from other answers and comments, but I think this recent discussion on Python-ideas and its continuation are the best answer to this question.

To summarize: the idea was already discussed in the past, and the benefits did not seem enough to motivate the syntax change, considering:

  • increased language complexity and impact on learning curve

  • technical changes in all implementations: CPython, Jython, Pypy..

  • possible weird situations that extreme use of the synthax could lead

One point that people seem to highly consider is to avoid bringing Perl-alike complexity that compromise maintainability.

This message and this one nicely summarize possible alternatives (almost already appeared in this page as well) to a compound if-statement in for-loop:

# nested if
for l in lines:
    if l.startswith('example'):
        body

# continue, to put an accent on exceptional case
for l in lines:
    if not l.startswith('example'):
        continue
    body

# hacky way of generator expression
# (better than comprehension as does not store a list)
for l in (l for l in lines if l.startswith('example')):
    body()

# and its named version
def gen(lines):
    return (l for l in lines if l.startswith('example'))
for line in gen(lines):
    body

# functional style
for line in filter(lambda l: l.startswith('example'), lines):
    body()
like image 56
Pintun Avatar answered Oct 21 '22 01:10

Pintun


Maybe not Pythonic, but you could filter the lines.

for line in filter(lambda l: l.startswith('example'), lines):
    print(line)

And you could define your own filter function, of course, if that lambda bothers you, or you want more complex filtering.

def my_filter(line):
    return line.startswith('example') or line.startswith('sample')

for line in filter(my_filter, lines):
    print(line)

I would say that having the condition within the loop is better because you aren't maintaining the "filtered" list in memory as you iterate over the lines.

So, that'd just be

for line in file:
    if not my_filter(line):
        continue
    # statements
like image 37
OneCricketeer Avatar answered Oct 21 '22 01:10

OneCricketeer