[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