Remove excess indentation.
This commit is contained in:
parent
0cfa95c9e9
commit
5c1080518d
384
pep-0548.rst
384
pep-0548.rst
|
@ -14,260 +14,260 @@ Post-History:
|
|||
Abstract
|
||||
========
|
||||
|
||||
This PEP proposes enhancing the ``break`` and ``continue`` 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.
|
||||
This PEP proposes enhancing the ``break`` and ``continue`` 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 rejected :pep:`315`:
|
||||
Quoting from the rejected :pep:`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::
|
||||
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::
|
||||
|
||||
<setup code>
|
||||
while <condition>:
|
||||
<loop body>
|
||||
<setup code>
|
||||
|
||||
That PEP was rejected because no syntax was found that was superior
|
||||
to the following form::
|
||||
|
||||
while True:
|
||||
<setup code>
|
||||
if not <condition>:
|
||||
break
|
||||
<setup code>
|
||||
while <condition>:
|
||||
<loop body>
|
||||
<setup code>
|
||||
|
||||
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.
|
||||
That PEP was rejected because no syntax was found that was superior
|
||||
to the following form::
|
||||
|
||||
while True:
|
||||
<setup code>
|
||||
if not <condition>:
|
||||
break
|
||||
<loop body>
|
||||
|
||||
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::
|
||||
The syntax of the break and continue statements are extended
|
||||
as follows::
|
||||
|
||||
break_stmt : "break" ["if" expression]
|
||||
continue_stmt : "continue" ["if" expression]
|
||||
break_stmt : "break" ["if" expression]
|
||||
continue_stmt : "continue" ["if" expression]
|
||||
|
||||
In addition, the syntax of the while statement is modified as follows::
|
||||
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
|
||||
while_stmt : while1_stmt|while2_stmt
|
||||
while1_stmt : "while" expression ":" suite
|
||||
["else" ":" suite]
|
||||
while2_stmt : "while" ":" suite
|
||||
|
||||
|
||||
Semantics
|
||||
=========
|
||||
|
||||
A ``break if`` or ``continue if`` is executed if and only if
|
||||
``expression`` evaluates to true.
|
||||
A ``break if`` or ``continue if`` is executed if and only if
|
||||
``expression`` evaluates to true.
|
||||
|
||||
A `while` statement with no expression loops until a break or return
|
||||
is executed (or an error is raised), as if it were a `'while True``
|
||||
statement. Given that the loop can never terminate except in a
|
||||
way that would not cause an ``else`` suite to execute, no ``else``
|
||||
suite is allowed in the expressionless form. If practical, it
|
||||
should also be an error if the body of an expressionless ``while``
|
||||
does not contain at least one ``break`` or ``return`` statement.
|
||||
A `while` statement with no expression loops until a break or return
|
||||
is executed (or an error is raised), as if it were a `'while True``
|
||||
statement. Given that the loop can never terminate except in a
|
||||
way that would not cause an ``else`` suite to execute, no ``else``
|
||||
suite is allowed in the expressionless form. If practical, it
|
||||
should also be an error if the body of an expressionless ``while``
|
||||
does not contain at least one ``break`` or ``return`` statement.
|
||||
|
||||
|
||||
Justification and Examples
|
||||
==========================
|
||||
|
||||
The previous "best possible" form::
|
||||
The previous "best possible" form::
|
||||
|
||||
while True:
|
||||
<setup code>
|
||||
if not <condition>:
|
||||
break
|
||||
<loop body>
|
||||
while True:
|
||||
<setup code>
|
||||
if not <condition>:
|
||||
break
|
||||
<loop body>
|
||||
|
||||
could be formatted as::
|
||||
could be formatted as::
|
||||
|
||||
while True:
|
||||
<setup code>
|
||||
if not <condition>: break
|
||||
<loop body>
|
||||
while True:
|
||||
<setup code>
|
||||
if not <condition>: break
|
||||
<loop body>
|
||||
|
||||
This is superficially almost identical to the form proposed by this
|
||||
PEP::
|
||||
This is superficially almost identical to the form proposed by this
|
||||
PEP::
|
||||
|
||||
while:
|
||||
<setup code>
|
||||
break if not <condition>
|
||||
<loop body>
|
||||
while:
|
||||
<setup code>
|
||||
break if not <condition>
|
||||
<loop body>
|
||||
|
||||
The significant difference here is that the loop flow control
|
||||
keyword appears *first* in 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.
|
||||
The significant difference here is that the loop flow control
|
||||
keyword appears *first* in 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::
|
||||
For example, this is a common code pattern, taken in this case
|
||||
from the tarfile module::
|
||||
|
||||
while True:
|
||||
buf = self._read(self.bufsize)
|
||||
if not buf:
|
||||
break
|
||||
t.append(buf)
|
||||
while True:
|
||||
buf = self._read(self.bufsize)
|
||||
if not buf:
|
||||
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.
|
||||
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::
|
||||
With the new syntax this becomes::
|
||||
|
||||
while:
|
||||
buf = self._read(self.bufsize)
|
||||
break if not buf
|
||||
t.append(buf)
|
||||
while:
|
||||
buf = self._read(self.bufsize)
|
||||
break if not buf
|
||||
t.append(buf)
|
||||
|
||||
Reading this we first see the``break``, 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.
|
||||
Reading this we first see the``break``, 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::
|
||||
Further, consider a more complex example from sre_parse::
|
||||
|
||||
while True:
|
||||
c = self.next
|
||||
self.__next()
|
||||
if c is None:
|
||||
if not result:
|
||||
raise self.error("missing group name")
|
||||
raise self.error("missing %s, unterminated name" % terminator,
|
||||
len(result))
|
||||
if c == terminator:
|
||||
if not result:
|
||||
raise self.error("missing group name", 1)
|
||||
break
|
||||
result += c
|
||||
return result
|
||||
|
||||
This is the natural way to write this code given current Python
|
||||
loop control syntax. However, given ``break if``, it would be more
|
||||
natural to write this as follows::
|
||||
|
||||
while:
|
||||
c = self.next
|
||||
self.__next()
|
||||
break if c is None or c == terminator
|
||||
result += c
|
||||
if not result:
|
||||
raise self.error("missing group name")
|
||||
elif c is None:
|
||||
while True:
|
||||
c = self.next
|
||||
self.__next()
|
||||
if c is None:
|
||||
if not result:
|
||||
raise self.error("missing group name")
|
||||
raise self.error("missing %s, unterminated name" % terminator,
|
||||
len(result))
|
||||
return result
|
||||
if c == terminator:
|
||||
if not result:
|
||||
raise self.error("missing group name", 1)
|
||||
break
|
||||
result += c
|
||||
return result
|
||||
|
||||
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.
|
||||
This is the natural way to write this code given current Python
|
||||
loop control syntax. However, given ``break if``, it would be more
|
||||
natural to write this as follows::
|
||||
|
||||
The proposed syntax also provides a natural, Pythonic spelling of
|
||||
the classic ``repeat ... until <expression>`` construct found in
|
||||
other languages, and for which no good syntax has previously been
|
||||
found for Python::
|
||||
while:
|
||||
c = self.next
|
||||
self.__next()
|
||||
break if c is None or c == terminator
|
||||
result += c
|
||||
if not result:
|
||||
raise self.error("missing group name")
|
||||
elif c is None:
|
||||
raise self.error("missing %s, unterminated name" % terminator,
|
||||
len(result))
|
||||
return result
|
||||
|
||||
while:
|
||||
...
|
||||
break if <expression>
|
||||
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 tarfile module, for example, has a couple of "read until" loops like
|
||||
the following::
|
||||
The proposed syntax also provides a natural, Pythonic spelling of
|
||||
the classic ``repeat ... until <expression>`` construct found in
|
||||
other languages, and for which no good syntax has previously been
|
||||
found for Python::
|
||||
|
||||
while True:
|
||||
s = self.__read(1)
|
||||
if not s or s == NUL:
|
||||
break
|
||||
while:
|
||||
...
|
||||
break if <expression>
|
||||
|
||||
With the new syntax this would read more clearly::
|
||||
The tarfile module, for example, has a couple of "read until" loops like
|
||||
the following::
|
||||
|
||||
while:
|
||||
s = self.__read(1)
|
||||
break if not s or s == NUL
|
||||
|
||||
The case for extending this syntax to ``continue`` is less strong,
|
||||
but buttressed by the value of consistency.
|
||||
|
||||
It is much more common for a ``continue`` statement to be at the
|
||||
end of a multiline if suite, such as this example from zipfile ::
|
||||
|
||||
while True:
|
||||
try:
|
||||
self.fp = io.open(file, filemode)
|
||||
except OSError:
|
||||
if filemode in modeDict:
|
||||
filemode = modeDict[filemode]
|
||||
continue
|
||||
raise
|
||||
while True:
|
||||
s = self.__read(1)
|
||||
if not s or s == NUL:
|
||||
break
|
||||
|
||||
The only opportunity for improvement the new syntax would offer for
|
||||
this loop would be the omission of the ``True`` token.
|
||||
With the new syntax this would read more clearly::
|
||||
|
||||
On the other hand, consider this example from uuid.py::
|
||||
while:
|
||||
s = self.__read(1)
|
||||
break if not s or s == NUL
|
||||
|
||||
for i in range(adapters.length):
|
||||
ncb.Reset()
|
||||
ncb.Command = netbios.NCBRESET
|
||||
ncb.Lana_num = ord(adapters.lana[i])
|
||||
if win32wnet.Netbios(ncb) != 0:
|
||||
The case for extending this syntax to ``continue`` is less strong,
|
||||
but buttressed by the value of consistency.
|
||||
|
||||
It is much more common for a ``continue`` statement to be at the
|
||||
end of a multiline if suite, such as this example from zipfile ::
|
||||
|
||||
while True:
|
||||
try:
|
||||
self.fp = io.open(file, filemode)
|
||||
except OSError:
|
||||
if filemode in modeDict:
|
||||
filemode = modeDict[filemode]
|
||||
continue
|
||||
ncb.Reset()
|
||||
ncb.Command = netbios.NCBASTAT
|
||||
ncb.Lana_num = ord(adapters.lana[i])
|
||||
ncb.Callname = '*'.ljust(16)
|
||||
ncb.Buffer = status = netbios.ADAPTER_STATUS()
|
||||
if win32wnet.Netbios(ncb) != 0:
|
||||
continue
|
||||
status._unpack()
|
||||
bytes = status.adapter_address[:6]
|
||||
if len(bytes) != 6:
|
||||
continue
|
||||
return int.from_bytes(bytes, 'big')
|
||||
raise
|
||||
break
|
||||
|
||||
This becomes::
|
||||
The only opportunity for improvement the new syntax would offer for
|
||||
this loop would be the omission of the ``True`` token.
|
||||
|
||||
for i in range(adapters.length):
|
||||
ncb.Reset()
|
||||
ncb.Command = netbios.NCBRESET
|
||||
ncb.Lana_num = ord(adapters.lana[i])
|
||||
continue if win32wnet.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()
|
||||
continue if win32wnet.Netbios(ncb) != 0
|
||||
status._unpack()
|
||||
bytes = status.adapter_address[:6]
|
||||
continue if len(bytes) != 6
|
||||
return int.from_bytes(bytes, 'big')
|
||||
On the other hand, consider this example from uuid.py::
|
||||
|
||||
This example indicates that there are non-trivial use cases where
|
||||
``continue if`` also improves the readability of the loop code.
|
||||
for i in range(adapters.length):
|
||||
ncb.Reset()
|
||||
ncb.Command = netbios.NCBRESET
|
||||
ncb.Lana_num = ord(adapters.lana[i])
|
||||
if win32wnet.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()
|
||||
if win32wnet.Netbios(ncb) != 0:
|
||||
continue
|
||||
status._unpack()
|
||||
bytes = status.adapter_address[:6]
|
||||
if len(bytes) != 6:
|
||||
continue
|
||||
return int.from_bytes(bytes, 'big')
|
||||
|
||||
It is probably significant to note that all of the examples selected
|
||||
for this PEP were found by grepping the standard library for ``while
|
||||
True`` and ``continue``, and the relevant examples were found in
|
||||
the first four modules inspected.
|
||||
This becomes::
|
||||
|
||||
for i in range(adapters.length):
|
||||
ncb.Reset()
|
||||
ncb.Command = netbios.NCBRESET
|
||||
ncb.Lana_num = ord(adapters.lana[i])
|
||||
continue if win32wnet.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()
|
||||
continue if win32wnet.Netbios(ncb) != 0
|
||||
status._unpack()
|
||||
bytes = status.adapter_address[:6]
|
||||
continue if len(bytes) != 6
|
||||
return int.from_bytes(bytes, 'big')
|
||||
|
||||
This example indicates that there are non-trivial use cases where
|
||||
``continue if`` 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 for ``while
|
||||
True`` and ``continue``, and the relevant examples were found in
|
||||
the first four modules inspected.
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document is placed in the public domain.
|
||||
This document is placed in the public domain.
|
||||
|
|
Loading…
Reference in New Issue