Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 548 – Flexiblere Schleifenkontrolle

Autor:
R David Murray
Status:
Abgelehnt
Typ:
Standards Track
Erstellt:
05-Sep-2017
Python-Version:
3.7
Post-History:
05. Aug. 2017

Inhaltsverzeichnis

Ablehnungsnotiz

Ablehnung durch Guido: https://mail.python.org/pipermail/python-dev/2017-September/149232.html

Zusammenfassung

Diese PEP schlägt eine Erweiterung der Anweisungen break und continue um einen optionalen booleschen Ausdruck vor, der steuert, ob sie ausgeführt werden oder nicht. Dies ermöglicht eine klarere und kompaktere Ausdrucksweise des Kontrollflusses in Schleifen.

Motivation

Zitat aus der abgelehnten PEP 315

Es ist oft notwendig, dass ein Code vor jeder Auswertung der while-Schleifenbedingung ausgeführt wird. Dieser Code wird oft außerhalb der Schleife als Initialisierungscode dupliziert, der einmal vor dem Eintritt in die Schleife ausgeführt wird.
<setup code>
while <condition>:
    <loop body>
    <setup code>

Diese PEP wurde abgelehnt, da keine Syntax gefunden wurde, die der folgenden Form überlegen war:

while True:
    <setup code>
    if not <condition>:
        break
    <loop body>

Diese PEP schlägt eine überlegene Form vor, die auch auf for-Schleifen anwendbar ist. Sie ist überlegen, weil sie den Kontrollfluss in Schleifen expliziter macht und gleichzeitig die ästhetische Einrückung von Python beibehält.

Syntax

Die Syntax der break- und continue-Anweisungen wird wie folgt erweitert:

break_stmt : "break" ["if" expression]
continue_stmt : "continue" ["if" expression]

Zusätzlich wird die Syntax der while-Anweisung wie folgt modifiziert:

while_stmt : while1_stmt|while2_stmt
while1_stmt : "while" expression ":" suite
              ["else" ":" suite]
while2_stmt : "while" ":" suite

Semantik

Ein break if oder continue if wird genau dann ausgeführt, wenn expression zu wahr ausgewertet wird.

Eine while-Anweisung ohne Ausdruck läuft, bis eine break- oder return-Anweisung ausgeführt wird (oder ein Fehler auftritt), so als wäre es eine while True-Anweisung. Da die Schleife niemals auf eine Weise beendet werden kann, die keine Ausführung einer else-Suite verursachen würde, ist in der ausdruckslosen Form keine else-Suite erlaubt. Wenn praktisch, sollte es auch ein Fehler sein, wenn der Körper einer ausdruckslosen while-Schleife mindestens eine break- oder return-Anweisung enthält.

Begründung und Beispiele

Die vorherige "bestmögliche" Form

while True:
    <setup code>
    if not <condition>:
        break
    <loop body>

könnte formatiert werden als

while True:
    <setup code>
    if not <condition>: break
    <loop body>

Dies ist oberflächlich fast identisch mit der von dieser PEP vorgeschlagenen Form.

while:
    <setup code>
    break if not <condition>
    <loop body>

Der signifikante Unterschied hier ist, dass das Schlüsselwort zur Steuerung des Schleifenflusses *zuerst* in der Codezeile erscheint. Dies erleichtert es, den Kontrollfluss in der Schleife auf einen Blick zu erfassen, insbesondere beim Lesen von farbigem Code.

Beispielsweise ist dies ein gängiges Code-Muster, das in diesem Fall aus dem tarfile-Modul stammt:

while True:
    buf = self._read(self.bufsize)
    if not buf:
        break
    t.append(buf)

Beim Lesen hiervon sehen wir entweder das break und müssen möglicherweise darüber nachdenken, auf welche while-Schleife es sich bezieht, da das break unter dem if eingerückt ist, und dann rückwärts verfolgen, um die Bedingung zu lesen, die es auslöst; oder wir lesen die Bedingung und entdecken erst danach, dass diese Bedingung den Fluss der Schleife ändert.

Mit der neuen Syntax wird dies zu

while:
    buf = self._read(self.bufsize)
    break if not buf
    t.append(buf)

Beim Lesen hiervon sehen wir zuerst das break, das offensichtlich für die while-Schleife gilt, da es auf derselben Einrückungsebene wie der Schleifenkörper liegt, und dann lesen wir die Bedingung, die den Kontrollfluss ändert.

Betrachten Sie weiter ein komplexeres Beispiel aus 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

Dies ist die natürliche Art, diesen Code angesichts der aktuellen Python-Schleifenkontrollsyntax zu schreiben. Angesichts von break if wäre es jedoch natürlicher, dies wie folgt zu schreiben:

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

Diese Form verlagert die Fehlerbehandlung aus dem Schleifenkörper heraus und macht die Schleifenlogik verständlicher. Obwohl es sicherlich möglich wäre, den Code mit der aktuellen Syntax auf diese Weise zu schreiben, macht die vorgeschlagene Syntax es natürlicher, ihn in der klareren Form zu schreiben.

Die vorgeschlagene Syntax bietet auch eine natürliche, Python-typische Schreibweise des klassischen Konstrukts repeat ... until <expression>, das in anderen Sprachen zu finden ist und für das bisher keine gute Syntax für Python gefunden wurde.

while:
    ...
    break if <expression>

Das tarfile-Modul enthält beispielsweise ein paar "Lese bis"-Schleifen wie die folgenden:

while True:
    s = self.__read(1)
    if not s or s == NUL:
        break

Mit der neuen Syntax würde dies klarer lesbar sein:

while:
    s = self.__read(1)
    break if not s or s == NUL

Die Argumente für die Erweiterung dieser Syntax auf continue sind weniger stark, werden aber durch den Wert der Konsistenz gestützt.

Es ist viel üblicher, dass eine continue-Anweisung am Ende einer mehrzeiligen if-Suite steht, wie in diesem Beispiel aus zipfile:

while True:
    try:
        self.fp = io.open(file, filemode)
    except OSError:
        if filemode in modeDict:
            filemode = modeDict[filemode]
            continue
        raise
    break

Die einzige Verbesserung, die die neue Syntax für diese Schleife bieten würde, wäre die Auslassung des True-Tokens.

Betrachten Sie andererseits dieses Beispiel aus uuid.py:

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')

Dies wird zu

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')

Dieses Beispiel zeigt, dass es nicht-triviale Anwendungsfälle gibt, bei denen continue if auch die Lesbarkeit des Schleifencodes verbessert.

Es ist wahrscheinlich bemerkenswert, dass alle für diese PEP ausgewählten Beispiele durch Durchsuchen der Standardbibliothek nach while True und continue gefunden wurden, und die relevanten Beispiele wurden in den ersten vier inspizierten Modulen gefunden.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0548.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT