[Python-ideas] if in for-loop statement

Steven D'Aprano steve at pearwood.info
Thu Feb 23 23:28:10 EST 2017

On Thu, Feb 23, 2017 at 11:07:49PM +0100, Sven R. Kunze wrote:

> >If the use of two lines and two indents is truly a problem, to the point
> >that you really need to refactor to a single line, that's a code smell:
> >your function is probably too big, too complex and does too much, and
> >should be refactored.
> May I disagree with you here? I write many functions with a single for 
> loop + a single if+continue on a daily basis (usually generators).
> They don't seem to look like code-smell to me.

I'm sorry, I didn't explain myself well enough.

There is nothing wrong with a nested for...if pair of statements. But 
that does take two lines, and two indents, rather than one:

    for ...
        if ...

versus hypothetical:

    for ... if ...

The proposed syntax saves one line, and one indent. That is a saving, 
but it is not a big saving. If one line and one indent *really* makes a 
difference to a piece of code, it is probably because you are already 
deeply nested:

    class ...
        def ...
             def ...
                 try ...
                     try ...
                         while ...
                             try ...
                                 if ...
                                     with ...
                                         if ...
                                             while ...
                                                 for ...
                                                     if ...
                                                         # not enough 
                                                         # room here

That is already so deeply nested that saving one indent might be 
important. But the problem isn't the for...if section, it is the ten or 
a dozen *previous* statements. "I have run out of horizontal space" is 
the code-smell, not "I have a for loop and an if branch".

Most of the time, "save one line of code" or "save one indent" is not a 
big win. It it insignificant: most of the time, extra lines are cheap, 
and there is plenty of room for all the indent levels needed.

There is one obvious exception to the rule:

    if ...
        if ...
            if ...
                if ...

That can get pretty costly in terms of indent levels very quickly. And 
that is why Python has elif:

    if ...
    elif ...
    elif ...
    elif ...

So don't misunderstand me. I do appreciate that sometimes saving indents 
and lines is important. But what I am saying is that in *this* case, it 
is rarely important enough to matter, and when it does matter, your code 
probably has bigger problems than just the for...if blocks.

> Not saying they justify this feature but it's easier to read a positive 
> (including) if than a negative one (skipping).
> >Deeply nested for...for...for...if...if...if...for... blocks look ugly
> >because they are ugly. Allowing them all on one line makes the Python
> >language worse, not better. It is unfortunate that comprehensions, by
> >their nature, must allow that sort of thing, but that doesn't mean we
> >have to allow it elsewhere.
> Another disagreement here. Just because it's possible to do ugly stuff 
> with a hammer shouldn't mean we cannot allow hammers. It's an argument 
> neither in favor of nor against the proposal.

Since code is read more often than it is written, good programming 
languages are pretty programming languages. That's why most of us choose 
Python: its not the most efficient language, or fastest running 
language, or most powerful language, but it is fast enough, efficient 
enough, powerful enough, and most importantly, it is pretty. By which I 
mean it is relatively easy to read (at least for English-readers), which 
makes it easy to write, debug and maintain.

If we want to keep Python pretty, we should discourage things which are 
ugly. Discourage, or even outright ban them.

Some features are on the borderline. There is a grey area where it comes 
to the subjective aesthetic judgement of the language Dictator. I think 
that this is one of those areas, and I hope that we are correctly 
channelling the BDFL.

Because for...if...for...if... is on the boundary, we can allow it in 
one case (comprehensions) and forbid it in another (statements):

- in a series of nested statements, the benefit of allowing the 
  for...if all on one line is not enough to make up for the risk
  of people misusing it;

- in a comprehension, the benefit is greater, which is enough to
  make up for the risk of people misusing it.

Because a comprehension is an expression, adding an extra line and an 
extra indent is not usually helpful. It may mess up the expression it is 
embedded in and lead to messy code:

    result = some_function(arg, [comprehension
                                     that goes over
                                              lines and
                           spam, eggs, cheese) + 1

That's probably not helpful, and possibly even confusing. So for 
comprehensions, we can relax the rule about one indent level per for or 
if statement.

The reason we have different syntax for comprehensions and statements is 
that they are different kinds of code, used in different ways.


More information about the Python-ideas mailing list