[Python-ideas] for/except/else

Wolfgang Maier wolfgang.maier at biologie.uni-freiburg.de
Wed Mar 1 04:37:17 EST 2017


I know what the regulars among you will be thinking (time machine, high 
bar for language syntax changes, etc.) so let me start by assuring you 
that I'm well aware of all of this, that I did research the topic before 
posting and that this is not the same as a previous suggestion using 
almost the same subject line.

Now here's the proposal: allow an except (or except break) clause to 
follow for/while loops that will be executed if the loop was terminated 
by a break statement.

The idea is certainly not new. In fact, Nick Coghlan, in his blog post
http://python-notes.curiousefficiency.org/en/latest/python_concepts/break_else.html, 
uses it to provide a mental model for the meaning of the else following 
for/while, but, as far as I'm aware, he never suggested to make it legal 
Python syntax.

Now while it's possible that Nick had a good reason not to do so, I 
think there would be three advantages to this:

- as explained by Nick, the existence of "except break" would strengthen 
the analogy with try/except/else and help people understand what the 
existing else clause after a loop is good for.
There has been much debate over the else clause in the past, most 
prominently, a long discussion on this list back in 2009 (I recommend 
interested people to start with Steven D'Aprano's Summary of it at 
https://mail.python.org/pipermail/python-ideas/2009-October/006155.html) 
that shows that for/else is misunderstood by/unknown to many Python 
programmers.

- in some situations for/except/else would make code more readable by 
bringing logical alternatives closer together and to the same 
indentation level in the code. Consider a simple example (taken from the 
docs.python Tutorial:

for n in range(2, 10):
     for x in range(2, n):
         if n % x == 0:
             print(n, 'equals', x, '*', n//x)
             break
     else:
         # loop fell through without finding a factor
         print(n, 'is a prime number')

There are two logical outcomes of the inner for loop here - a given 
number can be either prime or not. However, the two code branches 
dealing with them end up at different levels of indentation and in 
different places, one inside and one outside the loop block. This second 
issue can become much more annoying in more complex code where the loop 
may contain additional code after the break statement.

Now compare this to:

for n in range(2, 10):
     for x in range(2, n):
         if n % x == 0:
             break
     except break:
         print(n, 'equals', x, '*', n//x)
     else:
         # loop fell through without finding a factor
         print(n, 'is a prime number')

IMO, this reflects the logic better.


- it could provide an elegant solution for the How to break out of two 
loops issue. This is another topic that comes up rather regularly 
(python-list, stackoverflow) and there is again a very good blog post 
about it, this time from Ned Batchelder at 
https://nedbatchelder.com/blog/201608/breaking_out_of_two_loops.html.
Stealing his example, here's code (at least) a newcomer may come up with 
before realizing it can't work:

s = "a string to examine"
for i in range(len(s)):
     for j in range(i+1, len(s)):
         if s[i] == s[j]:
             answer = (i, j)
             break   # How to break twice???

with for/except/else this could be written as:

s = "a string to examine"
for i in range(len(s)):
     for j in range(i+1, len(s)):
         if s[i] == s[j]:
             break
     except break:
         answer = (i, j)
         break


So much for the pros. Of course there are cons, too. The classical one 
for any syntax change, of course, is:
- burden on developers who have to implement and maintain the new 
syntax. Specifically, this proposal would make parsing/compiling of 
loops more complicated.

Others include:
- using except will make people think of exceptions and that may cause 
new confusion; while that's true, I would argue that, in fact, break and 
exceptions are rather similar features in that they are gotos in 
disguise, so except will still be used to catch an interruption in 
normal control flow.

- the new syntax will not help people understand for/else if except is 
not used; importantly, I'm *not* proposing to disallow the use of 
for/else without except (if that would ever happen it would be in the 
*very* distant future) so that would indeed mean that people would 
encounter for/else, not only in legacy, but also in newly written code. 
However, I would expect that they would also start seeing for/except 
increasingly (not least because it solves the "break out of two loops" 
issue) so they would be nudged towards thinking of the else after 
for/while more like the else in try/except/else just as Nick proposes 
it. Interestingly, there has been another proposal on this list several 
years ago about allowing try/else without except, which I liked at the 
time and which would have made try/except/]else work exactly as my 
proposed for/except/else. Here it is:
https://mail.python.org/pipermail/python-ideas/2011-November/012875.html

- as a result of previous discussions about for/else a section was added 
to PEP3099 saying:
"The else clause in while and for loops will not change semantics, or be 
removed."
However, the proposal here is not to change the else clause semantics, 
but add an additional except clause.

So that's it and while I'm well aware of the slim chances of this 
getting legal syntax, I would still be happy to get feedback from you :)

Best,
Wolfgang



More information about the Python-ideas mailing list