[Python-ideas] for/else statements considered harmful (original) (raw)

Nick Coghlan ncoghlan at gmail.com
Thu Jun 7 02:53:22 CEST 2012


On Thu, Jun 7, 2012 at 9:58 AM, Bruce Leban <bruce at leapyear.org> wrote:

If we could go back in time I would completely agree. But since we can't, flipping meaning of else would be too error inducing and therefore not at all likely.

The meaning of the "else:" clause on for and while loops is actually much closer to the sense in "try/except/else" sense than it is to the sense in "if/else".

Consider the following:

for x in range(20):
    if x > 10:
        break
else:
    # Reached the end of the loop

As an approximate short hand for:

class BreakLoop(Exception): pass

try:
    for x in range(20):
        if x > 10:
            raise BreakLoop
except BreakLoop:
    pass
else:
    # Reached the end of the loop

It's not implemented anything like that (and the analogy doesn't hold in many other respects), but in terms of the semantics of the respective else clauses it's an exact match.

Part of the problem is that the "else:" clause on while loops is often explained as follows (and I've certainly been guilty of this), which I now think exacerbates the confusion rather than reducing it:

The following code: x = 0 while x < 10: x += 1 if x == y: break else: # Made it to 10

Can be seen as equivalent to:

x = 0
while 1:
    if x < 10:
        pass
    else:
        # Made it to 10
    x += 1
    if x == y:
       break

This actually ends up reinforcing the erroneous connection to if statements, when we really need to be encouraging people to think of this clause in terms of try statements, with "break" playing the role of an exception being raised.

So I think what we actually have is a documentation problem where we need to be actively encouraging the "while/else", "for/else" -> "try/except/else" link and discouraging any attempts to think of this construct in terms of if statements (as that is a clear recipe for confusion).

If anything were to change at the language level, my preference would be to further reinforce the try/except/else connection by allowing an "except break" clause:

for x in range(20):
    if x > 10:
        break
except break:
    # Bailed out early
else:
    # Reached the end of the loop

To critique the specific proposal presented at the start of the thread, there are three main problems with it:

  1. It doesn't match the expected semantics of a "finally:" clause. In try/finally the finally clause executes regardless of how the suite execution is terminated (whether via an exception, reaching the end of the suite, or leaving the suite early via a return, break or continue control flow statement). That is explicitly not the case here (as a loop's else clause only executes in the case of normal loop termination - which precisely matches the semantics of the else clause in try/except/else)
  2. As Bruce pointed out, the meaning of the else: clause on loops can't be changed as it would break backwards compatibility with existing code
  3. The post doesn't explain how the proposed change in semantics also makes sense for while loops

Cheers, Nick.

-- Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia



More information about the Python-ideas mailing list