How can I make sense of the `else` clause of Python loops?

13,764

Solution 1

The while statement with an else clause

while condition:
    suite
else:
    exhausted

is exactly equivalent to

while True:
    if not condition:
        exhausted
        break
    suite

The for statement with an else clause

for item in iterable:
    suite
else:
    exhausted

is exactly equivalent to

iterator = iter(iterable)
try:
    while True:
        item = next(iterator)
        suite
except StopIteration:
    exhausted

It helps understand the effect of a break or continue statement in the suite statement.

Note. — For the while and for statements without an else clause, replace the exhausted statement with a pass statement in the equivalent code.

To memorise the meaning of the else clause, you can interpret a loop statement as

if loop_breaks:
    pass
else:
    exhausted

Solution 2

An if statement runs its else clause if its condition evaluates to false. Identically, a while loop runs the else clause if its condition evaluates to false.

This rule matches the behavior you described:

  • In normal execution, the while loop repeatedly runs until the condition evaluates to false, and therefore naturally exiting the loop runs the else clause.
  • When you execute a break statement, you exit out of the loop without evaluating the condition, so the condition cannot evaluate to false and you never run the else clause.
  • When you execute a continue statement, you evaluate the condition again, and do exactly what you normally would at the beginning of a loop iteration. So, if the condition is true, you keep looping, but if it is false you run the else clause.
  • Other methods of exiting the loop, such as return, do not evaluate the condition and therefore do not run the else clause.

for loops behave the same way. Just consider the condition as true if the iterator has more elements, or false otherwise.

Solution 3

Better to think of it this way: The else block will always be executed if everything goes right in the preceding for block such that it reaches exhaustion.

Right in this context will mean no exception, no break, no return. Any statement that hijacks control from for will cause the else block to be bypassed.


A common use case is found when searching for an item in an iterable, for which the search is either called off when the item is found or a "not found" flag is raised/printed via the following else block:

for items in basket:
    if isinstance(item, Egg):
        break
else:
    print("No eggs in basket")  

A continue does not hijack control from for, so control will proceed to the else after the for is exhausted.

Solution 4

When does an if execute an else? When its condition is false. It is exactly the same for the while/else. So you can think of while/else as just an if that keeps running its true condition until it evaluates false. A break doesn't change that. It just jumps out of the containing loop with no evaluation. The else is only executed if evaluating the if/while condition is false.

The for is similar, except its false condition is exhausting its iterator.

continue and break don't execute else. That isn't their function. The break exits the containing loop. The continue goes back to the top of the containing loop, where the loop condition is evaluated. It is the act of evaluating if/while to false (or for has no more items) that executes else and no other way.

Solution 5

This is what it essentially means:

for/while ...:
    if ...:
        break
if there was a break:
    pass
else:
    ...

It's a nicer way of writing of this common pattern:

found = False
for/while ...:
    if ...:
        found = True
        break
if not found:
    ...

The else clause will not be executed if there is a return because return leaves the function, as it is meant to. The only exception to that which you may be thinking of is finally, whose purpose is to be sure that it is always executed.

continue has nothing special to do with this matter. It causes the current iteration of the loop to end which may happen to end the entire loop, and clearly in that case the loop wasn't ended by a break.

try/else is similar:

try:
    ...
except:
    ...
if there was an exception:
    pass
else:
    ...
Share:
13,764

Related videos on Youtube

alexis
Author by

alexis

Updated on June 06, 2022

Comments

  • alexis
    alexis over 1 year

    Many Python programmers are probably unaware that the syntax of while loops and for loops includes an optional else: clause:

    for val in iterable:
        do_something(val)
    else:
        clean_up()
    

    The body of the else clause is a good place for certain kinds of clean-up actions, and is executed on normal termination of the loop: I.e., exiting the loop with return or break skips the else clause; exiting after a continue executes it. I know this only because I just looked it up (yet again), because I can never remember when the else clause is executed.

    Always? On "failure" of the loop, as the name suggests? On regular termination? Even if the loop is exited with return? I can never be entirely sure without looking it up.

    I blame my persisting uncertainty on the choice of keyword: I find else incredibly unmnemonic for this semantics. My question is not "why is this keyword used for this purpose" (which I would probably vote to close, though only after reading the answers and comments), but how can I think about the else keyword so that its semantics make sense, and I can therefore remember it?

    I'm sure there was a fair amount of discussion about this, and I can imagine that the choice was made for consistency with the try statement's else: clause (which I also have to look up), and with the goal of not adding to the list of Python's reserved words. Perhaps the reasons for choosing else will clarify its function and make it more memorable, but I'm after connecting name to function, not after historical explanation per se.

    The answers to this question, which my question was briefly closed as a duplicate of, contain a lot of interesting back story. My question has a different focus (how to connect the specific semantics of else with the keyword choice), but I feel there should be a link to this question somewhere.

    • Nick stands with Ukraine
      Nick stands with Ukraine almost 3 years
      @alexis The edit by Dharman was made in an attempt to make your question not opinion based while maintaining the question itself, IMO the edit makes the post an awful lot better (and not worthy of closure)
    • alexis
      alexis almost 3 years
      @Dharman, I appreciate your efforts but your edits completely distort the intent and content of the question. Please stop.
    • Nick stands with Ukraine
      Nick stands with Ukraine almost 3 years
      "how can I think about the else keyword so that its semantics make sense, and I can therefore remember it?" - Explaining to you specifically how we can help you personally remember how else works isn't a helpful question, Dharman is the only reason I retracted a close vote on the question, because without it the question is opinion based
    • alexis
      alexis almost 3 years
      Thank you for the explanation, @nick. Nevertheless, Dharman's title makes it a duplicate of a very different question. If you are convinced that this is too opinion-based, i can leave with your vote. But please leave the question alone.
    • alexis
      alexis almost 3 years
      Also the question is about making sense of this design, not about what it does.
  • alexis
    alexis over 7 years
    What you say sounds very sensible, but lumping the three termination conditions together, "until [the condition] is False or breaks/continues", is wrong: Crucially, the else clause is executed if the loop is exited with continue (or normally), but not if we exit with break. These subtleties are why I'm trying to really grok what else catches and what it does not.
  • alexis
    alexis over 7 years
    Sounds very nice... but then you'd expect an else clause to be executed when things don't go right, wouldn't you? I'm already getting confused again...
  • Tadhg McDonald-Jensen
    Tadhg McDonald-Jensen over 7 years
    I have to disagree with you on "Technically, It isn't [semantically similar to every other else]", since the else is run when none of the conditions in the for loop evaluate to True, as I demonstrate in my answer
  • Moses Koledoye
    Moses Koledoye over 7 years
    @TadhgMcDonald-Jensen You can also break the loop on a False. So the question of how the for is broken depends on the use case.
  • alexis
    alexis over 7 years
    That's right, I am asking for a way to somehow relate what happens to the English meaning of "else" (which is indeed reflected in other uses of else in python). You provide a good intuitive summary of what else does, @Moses, but not of how we could associate this behavior with "else". If a different keyword was used (e.g., nobreak as mentioned in this answer to a related question), it would be easier to make sense of.
  • alexis
    alexis over 7 years
    Right. But a loop runs multiple times, so it's a little unclear how you mean to apply this to a for-loop. Can you clarify?
  • Moses Koledoye
    Moses Koledoye over 7 years
    @alexis From the link you added, given the etymology of this else, I think we'll have to accept the status quo :)
  • alexis
    alexis over 7 years
    What status quo do you mean? I certainly wasn't aiming for a change in Python's syntax!
  • Moses Koledoye
    Moses Koledoye over 7 years
    That this else does not behave the way it sounds. And to understand it, you'll have to apply some abstraction like the nobreak everyone already proposed.
  • Mark Tolonen
    Mark Tolonen over 7 years
    @alexis yes I needed to clarify there. Edited. continue doesn't execute the else, but does return to the top of the loop which may then evaluate to false.
  • Mark Tolonen
    Mark Tolonen over 7 years
    It really has nothing to do with "things going right". The else is purely executed when the if/while condition evaluates to false or for is out of items. break exists the containing loop (after the else). continue goes back and evaluates the loop condition again.
  • Tadhg McDonald-Jensen
    Tadhg McDonald-Jensen over 7 years
    @MosesKoledoye ok so in a chain of if/elifs the only way to break out of the chain is for one of the conditionals to be True, where as a for loop is exited with the explicit break (and both are broken by return or exception) I certainly don't think of things going "wrong" when the for loop needs to break.
  • Moses Koledoye
    Moses Koledoye over 7 years
    Going right is an oversimplification, and I already described what I meant by that in the answer.
  • alexis
    alexis over 7 years
    Where is the else? If you meant the done: label to stand proxy or else:, I believe you have it exactly backwards.
  • zwol
    zwol over 7 years
    @alexis The 'else' code would fill in the '...' immediately before the done: label. The overall correspondence is, maybe, best said thus: Python has the else-on-loop construct so that you can express this control flow pattern without goto.
  • supercat
    supercat over 7 years
    I 'd suggest that it could be enhanced by saying that the usual purpose of a for/else loop is to examine items until you've found what you're looking for and want to stop, or you run out of items. The "else" exists to handle the "you run out of items (without having found what you were looking for)" part.
  • Fabian Fagerholm
    Fabian Fagerholm over 7 years
    @supercat: Could be, but I don't know what the most common uses are out there. The else could also be used to do something when you're simply finished with all the items. Examples include writing a log entry, updating a user interface, or signaling some other process that you're done. Anything, really. Also, some pieces of code has the "successful" case end with a break inside the loop, and the else is used to handle the "error" case where you didn't find any suitable item during the iteration (maybe that was what you were thinking of?).
  • supercat
    supercat over 7 years
    The case I was thinking of was precisely the case where the successful case ends with a "break", and the "else" handles a lack of success. If there's no "break" within a loop, the "else" code may as well simply follow the loop as part of the enclosing block.
  • alexis
    alexis over 7 years
    There are other ways to execute this control flow pattern, e.g. by setting a flag. That's what the else avoids.
  • Fabian Fagerholm
    Fabian Fagerholm over 7 years
    Unless you need to distinguish between the case where the loop went through all the iterable items without interruption (and that was a successful case) and the case where it didn't. Then you have to put the "finalising" code in the loop's else block, or keep track of the result using other means. I basically agree, I'm just saying I don't know how people use this feature and therefore I'd like to avoid making assumptions of whether the "else handles successful case" scenario or the "else handles unsuccessful case" scenario is more common. But you have a good point, so comment upvoted!
  • Bergi
    Bergi over 7 years
    I pretty much like this answer, but you can simplify: Omit the end label and just put the goto loop inside the if body. Maybe even outdent by putting the if on the same line as the label, and it suddenly looks very much like the orignal.
  • Keiwan
    Keiwan over 7 years
    @Bergi Yes, I think that makes it a bit clearer, thanks.
  • Nomenator
    Nomenator over 7 years
    This is a most excellent answer. Treat your loops like a series of elif statements and the else behaviour will expose its natural logic.
  • alexis
    alexis over 7 years
    I like that, I think you're on to something. It ties in a little with how looping used to be implemented in the bad old days before loop keywords. (Namely: the check was placed at the bottom of the loop, with a goto the top on success.) But it's a shorter version of the top-voted answer...
  • Winston Ewert
    Winston Ewert over 7 years
    @alexis, subjective, but I find my way of expressing it easier to think about.
  • alexis
    alexis over 7 years
    actually I agree. If only because it's pithier.
  • alexis
    alexis over 7 years
    I also like this answer, but it is not drawing an analogy with a series of elif statements. There's an answer that does, and it has one net upvote.
  • Tadhg McDonald-Jensen
    Tadhg McDonald-Jensen over 7 years
    @alexis I have redone my answer, I think it is a lot clearer now.
  • Tadhg McDonald-Jensen
    Tadhg McDonald-Jensen over 7 years
    well not exactly, a while loop could have the condition meet False right before it breaks, in which case the else would not run but the condition is False. Similarly with for loops it can break on the last element.