Behavior of the for-else construct

Chris Angelico rosuav at gmail.com
Thu Mar 3 19:43:39 EST 2022


On Fri, 4 Mar 2022 at 11:14, Rob Cliffe via Python-list
<python-list at python.org> wrote:
>
> I find it so hard to remember what `for ... else` means that on the very
> few occasions I have used it, I ALWAYS put a comment alongside/below the
> `else` to remind myself (and anyone else unfortunate enough to read my
> code) what triggers it, e.g.
>
>      for item in search_list:
>          ...
>          ... break
>      else: # if no item in search_list matched the criteria

A "break" statement always takes you to the line following the current
loop construct. The "else" block is part of the current loop
construct. It's no more a "no-break" block than the body of the for
loop is a "no-break" body here:

for item in stuff:
    if condition: break
    frobnicate(item) # if no previous item matched the condition

> You get the idea.
> If I really want to remember what this construct means, I remind myself
> that `else` here really means `no break`.  Would have been better if it
> had been spelt `nobreak` or similar in the first place.

Maybe, but I think that obscures the meaning of it; "finally" isn't
quite right either (in a try block, you'd hit a finally block whether
you raise an exception or not), but I think it's closer. Creating a
new language keyword is an incredibly high cost.

Think of it like this:

for item in search_list:
    if condition: pass
    else:
        print("Condition not true for this item")

for item in search_list:
    if condition: break
else:
    print("Condition not true for any item")

There's a parallel here. Since a for-else loop is basically useless
without an if-break construct inside it, the else clause can be
thought of as the else on a massive if/elif chain:

if stuff[0].is_good:
    print("This item is good", stuff[0])
elif stuff[1].is_good:
    print("This item is good", stuff[1])
...
...
elif stuff[n].is_good:
    print("This item is good", stuff[n])
else:
    print("All items are bad")

As a loop, this looks like this:

for item in stuff:
    if item.is_good:
        print("This item is good", item)
        break
else:
    print("All items are bad")

The else is attached to the for so that it compasses ALL the if
statements, but it's still broadly saying "do this when we don't hit
the 'if' condition".

Whether that's a sufficient mnemonic, I don't know, but it doesn't
really matter; the construct is useful to those of us who want it, and
if other people ignore it, that's fine. Nobody ever said you had to
use or understand every single feature of the language you're using.

ChrisA


More information about the Python-list mailing list