PEP 3136 – Labeled break und continue
- Autor:
- Matt Chisholm <matt-python at theory.org>
- Status:
- Abgelehnt
- Typ:
- Standards Track
- Erstellt:
- 30-Jun-2007
- Python-Version:
- 3.1
- Post-History:
Ablehnungsbescheid
Diese PEP wird abgelehnt. Siehe https://mail.python.org/pipermail/python-3000/2007-July/008663.html.
Zusammenfassung
Diese PEP schlägt die Unterstützung für Labels in Pythons break und continue Anweisungen vor. Sie ist inspiriert von gelabelten break und continue in anderen Sprachen und dem eigenen, seltenen, aber anhaltenden Bedarf des Autors an einer solchen Funktion.
Einleitung
Die break Anweisung erlaubt es dem Programmierer, eine Schleife vorzeitig zu beenden, und die continue Anweisung erlaubt es dem Programmierer, vorzeitig zur nächsten Iteration einer Schleife zu springen. In Python derzeit können break und continue nur auf die innerste umschließende Schleife angewendet werden.
Die Unterstützung für Labels zu den break und continue Anweisungen hinzuzufügen, ist eine logische Erweiterung des bestehenden Verhaltens der break und continue Anweisungen. Gelabelte break und continue können die Lesbarkeit und Flexibilität von komplexem Code, der verschachtelte Schleifen verwendet, verbessern.
Der Kürze halber beziehen sich die Beispiele und die Diskussion in dieser PEP normalerweise auf die break Anweisung. Alle Beispiele und Begründungen gelten jedoch gleichermaßen für gelabelte continue.
Motivation
Wenn der Programmierer zur nächsten Iteration einer äußeren umschließenden Schleife wechseln oder mehrere Schleifen auf einmal beenden möchte, hat er einige weniger elegante Optionen.
Hier ist eine gängige Methode, um gelabelte break in Python zu imitieren (Für dieses und zukünftige Beispiele bezeichnet ... eine beliebige Anzahl von dazwischenliegenden Codezeilen)
for a in a_list:
time_to_break_out_of_a = False
...
for b in b_list:
...
if condition_one(a, b):
break
...
if condition_two(a, b):
time_to_break_out_of_a = True
break
...
if time_to_break_out_of_a:
break
...
Dies erfordert fünf Zeilen und eine zusätzliche Variable, time_to_break_out_of_a, um zu verfolgen, wann aus der äußeren (a) Schleife ausgebrochen werden soll. Und diese fünf Zeilen sind über viele Codezeilen verteilt, was den Kontrollfluss schwer verständlich macht.
Diese Technik ist auch fehleranfällig. Ein Programmierer, der diesen Code modifiziert, könnte versehentlich neuen Code nach dem Ende der inneren (b) Schleife, aber vor der Prüfung von time_to_break_out_of_a einfügen, anstatt nach der Prüfung. Dies bedeutet, dass Code, der durch das Ausbrechen aus der äußeren Schleife übersprungen werden sollte, fälschlicherweise ausgeführt wird.
Dies könnte auch mit einer Ausnahme geschrieben werden. Der Programmierer würde eine spezielle Ausnahme deklarieren, die innere Schleife in ein try einschließen und die Ausnahme abfangen und breaken, wenn er sie sieht
class BreakOutOfALoop(Exception): pass
for a in a_list:
...
try:
for b in b_list:
...
if condition_one(a, b):
break
...
if condition_two(a, b):
raise BreakOutOfALoop
...
except BreakOutOfALoop:
break
...
Aber wieder einmal erfordert dies fünf Zeilen und eine neue, nur für diesen Zweck vorgesehene Ausnahmeklasse (anstelle einer neuen Variablen) und verteilt den grundlegenden Kontrollfluss über viele Zeilen. Und es bricht die innere Schleife mit break ab und die andere Schleife mit einer Ausnahme, was unschön ist. [1]
Die nächste Strategie ist vielleicht die eleganteste Lösung, vorausgesetzt, condition_two() ist kostengünstig zu berechnen
for a in a_list:
...
for b in b_list:
...
if condition_one(a, b):
break
...
if condition_two(a, b):
break
...
if condition_two(a, b)
break
...
Zweimal zu breaken ist immer noch unschön. Diese Implementierung stützt sich auch auf die Tatsache, dass die innere (b) Schleife `b` in die äußere for-Schleife „leakt“, was (obwohl explizit unterstützt) sowohl für Anfänger überraschend als auch meiner Meinung nach kontraintuitiv und schlechte Praxis ist.
Der Programmierer muss sich auch immer noch merken, beide Breaks bei Bedingung zwei einzufügen und keinen Code vor dem zweiten Break einzufügen. Eine einzelne konzeptionelle Aktion, das Ausbrechen aus beiden Schleifen bei condition_two(), erfordert vier Codezeilen auf zwei Einrückungsebenen, möglicherweise getrennt durch viele dazwischenliegende Zeilen am Ende der inneren (b) Schleife.
Andere Sprachen
Lassen Sie nun jeglichen Widerwillen, den Sie vielleicht gegen andere Programmiersprachen haben, beiseite und betrachten Sie die Syntax von gelabeltem break und continue. In Perl
ALOOP: foreach $a (@a_array){
...
BLOOP: foreach $b (@b_array){
...
if (condition_one($a,$b)){
last BLOOP; # same as plain old last;
}
...
if (condition_two($a,$b)){
last ALOOP;
}
...
}
...
}
(Anmerkungen: Perl verwendet last anstelle von break. Die BLOOP-Labels könnten weggelassen werden; last und continue beziehen sich standardmäßig auf die innerste Schleife.)
PHP verwendet eine Zahl, die die Anzahl der Schleifen angibt, aus denen ausgebrochen werden soll, anstelle eines Labels
foreach ($a_array as $a){
....
foreach ($b_array as $b){
....
if (condition_one($a, $b)){
break 1; # same as plain old break
}
....
if (condition_two($a, $b)){
break 2;
}
....
}
...
}
C/C++, Java und Ruby haben alle ähnliche Konstruktionen.
Der Kontrollfluss in Bezug auf das Ausbrechen aus der äußeren (a) Schleife ist vollständig in der break Anweisung gekapselt, die ausgeführt wird, wenn die Abbruchbedingung erfüllt ist. Die Tiefe der break Anweisung spielt keine Rolle. Der Kontrollfluss ist nicht verstreut. Es werden keine zusätzlichen Variablen, Ausnahmen oder erneuten Überprüfungen oder Speicherung von Kontrollbedingungen benötigt. Es besteht keine Gefahr, dass Code versehentlich nach dem Ende der inneren (b) Schleife und vor der erneuten Überprüfung der Abbruchbedingung innerhalb der äußeren (a) Schleife eingefügt wird. Dies sind die Vorteile, die gelabelte break und continue für Python bringen würden.
Was dieser PEP nicht ist
Diese PEP ist kein Vorschlag zur Einführung von GOTO in Python. GOTO erlaubt es einem Programmierer, zu einem beliebigen Block oder einer beliebigen Codezeile zu springen, und erschwert im Allgemeinen den Kontrollfluss. Obwohl break und continue (mit oder ohne Unterstützung für Labels) als eine Art GOTO betrachtet werden können, ist es viel stärker eingeschränkt. Eine andere Python-Konstruktion, yield, könnte ebenfalls als eine Form von GOTO betrachtet werden – eine noch weniger eingeschränkte. Das Ziel dieser PEP ist es, eine Erweiterung der bestehenden Kontrollflusswerkzeuge break und continue vorzuschlagen, um den Kontrollfluss leichter verständlich zu machen, nicht schwieriger.
Gelabelte break und continue können keine Kontrolle auf eine andere Funktion oder Methode übertragen. Sie können nicht einmal die Kontrolle auf eine beliebige Zeile Code im aktuellen Geltungsbereich übertragen. Derzeit können sie nur das Verhalten einer Schleife beeinflussen und sind viel anders und viel stärker eingeschränkt als GOTO. Diese Erweiterung erlaubt es ihnen, jede umschließende Schleife im aktuellen Namensraum zu beeinflussen, ändert aber nicht ihr Verhalten zu dem von GOTO.
Spezifikation
Unter all diesen Vorschlägen werden break und continue von sich aus weiterhin so funktionieren, wie sie es derzeit tun, und sich standardmäßig auf die innerste Schleife beziehen.
Vorschlag A – Explizite Labels
Die Syntax von for- und while-Schleifen wird von einem optionalen Schlüsselwort as oder label (kontextbezogen) [2] und dann einem Bezeichner gefolgt, der verwendet werden kann, um die Schleife zu identifizieren, aus der ausgebrochen werden soll (oder die fortgesetzt werden soll).
Die break (und continue) Anweisungen werden von einem optionalen Bezeichner gefolgt, der sich auf die Schleife bezieht, aus der ausgebrochen werden soll (oder die fortgesetzt werden soll). Hier ist ein Beispiel mit dem Schlüsselwort as
for a in a_list as a_loop:
...
for b in b_list as b_loop:
...
if condition_one(a, b):
break b_loop # same as plain old break
...
if condition_two(a, b):
break a_loop
...
...
Oder mit label anstelle von as
for a in a_list label a_loop:
...
for b in b_list label b_loop:
...
if condition_one(a, b):
break b_loop # same as plain old break
...
if condition_two(a, b):
break a_loop
...
...
Dies hat alle oben genannten Vorteile. Es erfordert Änderungen an der Sprachsyntax: die Syntax von break und continue Anweisungen sowie for- und while-Anweisungen. Es erfordert entweder ein neues bedingtes Schlüsselwort label oder eine Erweiterung des bedingten Schlüsselworts as. [3] Es ist unwahrscheinlich, dass Änderungen an bestehenden Python-Programmen erforderlich sind. Das Übergeben eines Bezeichners, der nicht im lokalen Geltungsbereich definiert ist, an break oder continue würde einen NameError auslösen.
Vorschlag B – Numerisches break & continue
Anstatt die Syntax von for und while Schleifen zu ändern, würden break und continue ein numerisches Argument erhalten, das die gesteuerte umschließende Schleife angibt, ähnlich wie bei PHP.
Mir scheint es pythonischer, wenn break und continue sich auf Schleifen beziehen, die bei Null indiziert sind, im Gegensatz zur Indizierung bei Eins, wie PHP es tut.
for a in a_list:
...
for b in b_list:
...
if condition_one(a,b):
break 0 # same as plain old break
...
if condition_two(a,b):
break 1
...
...
Das Übergeben einer zu großen Zahl, einer Zahl kleiner als Null oder einer nicht-ganzen Zahl an break oder continue würde (wahrscheinlich) einen IndexError auslösen.
Dieser Vorschlag würde keine Änderungen an bestehenden Python-Programmen erfordern.
Vorschlag C – Die reduplizierende Methode
Die Syntax von break und continue würde geändert werden, um mehrere break und continue Anweisungen in derselben Zeile zu erlauben. Somit würde break break aus der ersten und zweiten umschließenden Schleife ausbrechen.
for a in a_list:
...
for b in b_list:
...
if condition_one(a,b):
break # plain old break
...
if condition_two(a,b):
break break
...
...
Dies würde es dem Programmierer auch ermöglichen, aus der inneren Schleife auszubrechen und die nächste äußere Schleife fortzusetzen, indem er einfach break continue schreibt, [4] und so weiter. Ich bin mir nicht sicher, welche Ausnahme ausgelöst würde, wenn der Programmierer mehr break oder continue Anweisungen verwendet als vorhandene Schleifen (vielleicht ein SyntaxError?).
Ich erwarte, dass dieser Vorschlag abgelehnt wird, da er als zu schwer verständlich eingestuft wird.
Dieser Vorschlag würde keine Änderungen an bestehenden Python-Programmen erfordern.
Vorschlag D – Explizite Iteratoren
Anstatt for- und while-Schleifen-Syntax mit Labels zu verzieren, müsste der Programmierer, der gelabelte Breaks verwenden möchte, den Iterator explizit erstellen und ihn einem Bezeichner zuweisen, wenn er aus einer tieferen Schleife ausbrechen oder diese fortsetzen möchte.
a_iter = iter(a_list)
for a in a_iter:
...
b_iter = iter(b_list)
for b in b_iter:
...
if condition_one(a,b):
break b_iter # same as plain old break
...
if condition_two(a,b):
break a_iter
...
...
Das Übergeben eines Nicht-Iterator-Objekts an break oder continue würde einen TypeError auslösen; und ein nicht vorhandener Bezeichner würde einen NameError auslösen. Dieser Vorschlag erfordert nur eine zusätzliche Zeile zum Erstellen einer gelabelten Schleife und keine zusätzlichen Zeilen zum Ausbrechen aus einer enthaltenden Schleife und keine Änderungen an bestehenden Python-Programmen.
Vorschlag E – Explizite Iteratoren und Iterator-Methoden
Dies ist eine Variante von Vorschlag D. Iteratoren müssten explizit erstellt werden, wenn etwas anderes als die grundlegendste Verwendung von break und continue erforderlich wäre. Anstatt die Syntax von break und continue zu ändern, könnten den Iterator-Typen Methoden .break() und .continue() hinzugefügt werden.
a_iter = iter(a_list)
for a in a_iter:
...
b_iter = iter(b_list)
for b in b_iter:
...
if condition_one(a,b):
b_iter.break() # same as plain old break
...
if condition_two(a,b):
a_iter.break()
...
...
Ich erwarte, dass dieser Vorschlag wegen seiner schieren Hässlichkeit abgelehnt wird. Er erfordert jedoch keinerlei Änderungen an der Sprachsyntax und auch keine Änderungen an bestehenden Python-Programmen.
Implementierung
Ich habe mich noch nie mit der Implementierung der Python-Sprache beschäftigt, daher habe ich keine Ahnung, wie schwierig dies zu implementieren wäre. Wenn diese PEP akzeptiert wird, aber niemand verfügbar ist, um die Funktion zu schreiben, werde ich versuchen, sie selbst zu implementieren.
Fußnoten
Ressourcen
Dieses Thema ist bereits aufgetaucht, obwohl es meines Wissens nie gelöst wurde.
- gelabelte Breaks, auf comp.lang.python, im Kontext von
do...whileSchleifen - break LABEL vs. exceptions + PROPOSAL, auf python-list, verglichen mit der Verwendung von Exceptions für die Ablaufsteuerung
- Benannte Codeblöcke auf python-list, ein Vorschlag, der durch den Wunsch nach gelabeltem Break / Continue motiviert ist
- mod_python Bugfix Ein Beispiel für jemanden, der ein Flag innerhalb einer inneren Schleife setzt, das ein Continue in der umschließenden Schleife auslöst, um den Mangel an gelabeltem Break und Continue zu umgehen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-3136.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT