PEP 548 – More Flexible Loop Control
- Author:
- R David Murray
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 05-Sep-2017
- Python-Version:
- 3.7
- Post-History:
- 05-Aug-2017
Table of Contents
Rejection Note
Rejection by Guido: https://mail. Python.org/pipermail/ Python -dev/2017-September/149232.html
Abstract
This PEP proposes enhancing thebreak
andcontinue
statements
with an optional boolean expression that controls whether or not
they execute. This allows the flow of control in loops to be
expressed more clearly and compactly.
Motivation
Quoting from the rejectedPEP 315:
It is often necessary for some code to be executed before each evaluation of the while loop condition. This code is often duplicated outside the loop, as setup code that executes once before entering the loop:<setupcode> while<condition>: <loopbody> <setupcode>
That PEP was rejected because no syntax was found that was superior to the following form:
whileTrue:
<setupcode>
ifnot<condition>:
break
<loopbody>
This PEP proposes a superior form, one that also has application to for loops. It is superior because it makes the flow of control in loops more explicit, while preserving Python’s indentation aesthetic.
Syntax
The syntax of the break and continue statements are extended as follows:
break_stmt:"break"["if"expression]
continue_stmt:"continue"["if"expression]
In addition, the syntax of the while statement is modified as follows:
while_stmt:while1_stmt|while2_stmt
while1_stmt:"while"expression":"suite
["else"":"suite]
while2_stmt:"while"":"suite
Semantics
Abreakif
orcontinueif
is executed if and only if
expression
evaluates to true.
Awhile
statement with no expression loops until a break or return
is executed (or an error is raised), as if it were awhileTrue
statement. Given that the loop can never terminate except in a
way that would not cause anelse
suite to execute, noelse
suite is allowed in the expressionless form. If practical, it
should also be an error if the body of an expressionlesswhile
does not contain at least onebreak
orreturn
statement.
Justification and Examples
The previous “best possible” form:
whileTrue:
<setupcode>
ifnot<condition>:
break
<loopbody>
could be formatted as:
whileTrue:
<setupcode>
ifnot<condition>:break
<loopbody>
This is superficially almost identical to the form proposed by this PEP:
while:
<setupcode>
breakifnot<condition>
<loopbody>
The significant difference here is that the loop flow control keyword appearsfirstin the line of code. This makes it easier to comprehend the flow of control in the loop at a glance, especially when reading colorized code.
For example, this is a common code pattern, taken in this case from the tarfile module:
whileTrue:
buf=self._read(self.bufsize)
ifnotbuf:
break
t.append(buf)
Reading this, we either see the break and possibly need to think about where the while is that it applies to, since the break is indented under the if, and then track backward to read the condition that triggers it; or, we read the condition and only afterward discover that this condition changes the flow of the loop.
With the new syntax this becomes:
while:
buf=self._read(self.bufsize)
breakifnotbuf
t.append(buf)
Reading this we first see thebreak
,which obviously applies to
the while since it is at the same level of indentation as the loop
body, and then we read the condition that causes the flow of control
to change.
Further, consider a more complex example from sre_parse:
whileTrue:
c=self.next
self.__next()
ifcisNone:
ifnotresult:
raiseself.error("missing group name")
raiseself.error("missing%s,unterminated name "%terminator,
len(result))
ifc==terminator:
ifnotresult:
raiseself.error("missing group name",1)
break
result+=c
returnresult
This is the natural way to write this code given current Python
loop control syntax. However, givenbreakif
,it would be more
natural to write this as follows:
while:
c=self.next
self.__next()
breakifcisNoneorc==terminator
result+=c
ifnotresult:
raiseself.error("missing group name")
elifcisNone:
raiseself.error("missing%s,unterminated name "%terminator,
len(result))
returnresult
This form moves the error handling out of the loop body, leaving the loop logic much more understandable. While it would certainly be possible to write the code this way using the current syntax, the proposed syntax makes it more natural to write it in the clearer form.
The proposed syntax also provides a natural, Pythonic spelling of
the classicrepeat...until<expression>
construct found in
other languages, and for which no good syntax has previously been
found for Python:
while:
...
breakif<expression>
The tarfile module, for example, has a couple of “read until” loops like the following:
whileTrue:
s=self.__read(1)
ifnotsors==NUL:
break
With the new syntax this would read more clearly:
while:
s=self.__read(1)
breakifnotsors==NUL
The case for extending this syntax tocontinue
is less strong,
but buttressed by the value of consistency.
It is much more common for acontinue
statement to be at the
end of a multiline if suite, such as this example from zipfile
whileTrue:
try:
self.fp=io.open(file,filemode)
exceptOSError:
iffilemodeinmodeDict:
filemode=modeDict[filemode]
continue
raise
break
The only opportunity for improvement the new syntax would offer for
this loop would be the omission of theTrue
token.
On the other hand, consider this example from uuid.py:
foriinrange(adapters.length):
ncb.Reset()
ncb.Command=netbios.NCBRESET
ncb.Lana_num=ord(adapters.lana[i])
ifwin32wnet.Netbios(ncb)!=0:
continue
ncb.Reset()
ncb.Command=netbios.NCBASTAT
ncb.Lana_num=ord(adapters.lana[i])
ncb.Callname='*'.ljust(16)
ncb.Buffer=status=netbios.ADAPTER_STATUS()
ifwin32wnet.Netbios(ncb)!=0:
continue
status._unpack()
bytes=status.adapter_address[:6]
iflen(bytes)!=6:
continue
returnint.from_bytes(bytes,'big')
This becomes:
foriinrange(adapters.length):
ncb.Reset()
ncb.Command=netbios.NCBRESET
ncb.Lana_num=ord(adapters.lana[i])
continueifwin32wnet.Netbios(ncb)!=0
ncb.Reset()
ncb.Command=netbios.NCBASTAT
ncb.Lana_num=ord(adapters.lana[i])
ncb.Callname='*'.ljust(16)
ncb.Buffer=status=netbios.ADAPTER_STATUS()
continueifwin32wnet.Netbios(ncb)!=0
status._unpack()
bytes=status.adapter_address[:6]
continueiflen(bytes)!=6
returnint.from_bytes(bytes,'big')
This example indicates that there are non-trivial use cases where
continueif
also improves the readability of the loop code.
It is probably significant to note that all of the examples selected
for this PEP were found by grepping the standard library forwhile
True
andcontinue
,and the relevant examples were found in
the first four modules inspected.
Copyright
This document is placed in the public domain.
Source:https://github / Python /peps/blob/main/peps/pep-0548.rst
Last modified:2023-09-09 17:39:29 GMT