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

Python Enhancement Proposals

PEP 601 – Verbot von return/break/continue, die aus finally ausbrechen

Autor:
Damien George, Batuhan Taskaya
Sponsor:
Alyssa Coghlan
Discussions-To:
Discourse thread
Status:
Abgelehnt
Typ:
Standards Track
Erstellt:
26-Aug-2019
Python-Version:
3.8
Post-History:
26-Aug-2019, 23-Sep-2019
Ersetzt-Durch:
765
Resolution:
Discourse-Nachricht

Inhaltsverzeichnis

Ablehnungsnotiz

Diese PEP wurde vom Steering Council mit 4 zu 4 Stimmen abgelehnt.

Guido's Argumente für die Ablehnung der PEP sind: „mir scheint, dass die meisten Sprachen diese Art von Konstrukt implementieren, aber Styleguides und/oder Linter haben, die es ablehnen. Ich würde einen Vorschlag unterstützen, dies zu PEP 8 hinzuzufügen“, und „ich merke an, dass die Spielbeispiele etwas irreführend sind – die Funktionalität, die nützlich sein kann, ist ein bedingtes return (oder break etc.) innerhalb eines finally-Blocks.“.

Zusammenfassung

Diese PEP schlägt vor, return, break und continue Anweisungen innerhalb einer finally Suite zu verbieten, wenn sie aus dem finally ausbrechen würden. Ihre Verwendung an einer solchen Stelle hebt stillschweigend jede aktive Ausnahme auf, die durch das finally ausgelöst wird, was zu unklarem Code und möglichen Fehlern führt.

continue wird in Python 3.7 derzeit nicht in einem finally unterstützt (aufgrund von Implementierungsproblemen), und der Vorschlag ist, die Unterstützung dafür in Python 3.8 nicht hinzuzufügen. Für return und break schlägt der Vorschlag vor, deren Verwendung in Python 3.9 zu verwerfen, in Python 3.10 eine Kompilierungswarnung auszugeben und deren Verwendung danach zu verbieten.

Motivation

Die Verwendung von return, break und continue innerhalb einer finally Suite führt zu einem Verhalten, das überhaupt nicht offensichtlich ist. Betrachten Sie die folgende Funktion

def foo():
    try:
        foo()
    finally:
        return

Dies wird sauber (ohne Ausnahme) zurückgegeben, obwohl es eine unendliche Rekursion gibt und eine Ausnahme innerhalb des try ausgelöst wird. Der Grund ist, dass das return innerhalb des finally stillschweigend jede Ausnahme aufhebt, die durch die finally Suite propagiert wird. Ein solches Verhalten ist unerwartet und überhaupt nicht offensichtlich. Diese Funktion ist äquivalent zu

def foo():
    try:
        foo()
    except:
        pass
    return

break und continue haben ein ähnliches Verhalten (sie unterdrücken Ausnahmen), wenn sie zu Code außerhalb der finally Suite springen. Zum Beispiel

def bar():
    while True:
        try:
            1 / 0
        finally:
            break

Dieses Verhalten widerspricht den folgenden Teilen von The Zen of Python

  • Explizit ist besser als implizit – Ausnahmen werden implizit unterdrückt
  • Lesbarkeit zählt – die Absicht des Codes ist nicht offensichtlich
  • Fehler sollten niemals stillschweigend durchgehen; es sei denn, sie werden explizit unterdrückt – Ausnahmen werden implizit unterdrückt

Wenn dieses Verhalten des Unterdrückens von Ausnahmen wirklich benötigt wird, kann stattdessen die explizite Form eines try-except verwendet werden, und dies macht den Code klarer.

Unabhängig von der Semantik ist die Implementierung von return/break/continue innerhalb einer finally Suite nicht trivial, da sie zur Laufzeit korrekt aktive Ausnahmen verfolgen muss (eine auszuführende finally Suite hat möglicherweise eine aktive Ausnahme oder auch nicht) und diese entsprechend abbrechen muss. CPython hatte einen Fehler in diesem Fall für continue und hat es daher ursprünglich nicht erlaubt [1]. Das Erfordernis eines korrekten Verhaltens für return/break/continue innerhalb eines finally legt eine unnötige Last auf alternative Implementierungen von Python.

Andere Sprachen

Java erlaubt das Zurückgeben aus einem finally-Block, aber dessen Verwendung wird gemäß [2], [3], [4] entmutigt. Der Java-Compiler enthielt später eine Linting-Option -Xlint:finally, um vor der Verwendung von return in einem finally-Block zu warnen. Der Eclipse-Editor warnt ebenfalls vor dieser Verwendung.

Ruby erlaubt return von innerhalb von ensure (Python's finally), aber es sollte ein explizites return sein. Es wird entmutigt und von Lintern behandelt [5], [6].

Wie Ruby erlaubt auch JavaScript die Verwendung von return/break/continue innerhalb eines finally, aber es wird als unsicher angesehen und von eslint behandelt [7].

C# verbietet die Verwendung von beendenden Anweisungen wie return/goto/break innerhalb eines finally [8], [9].

Begründung

Da das Verhalten von return/break/continue innerhalb eines finally unklar ist, das Muster selten verwendet wird und es eine einfache Alternative gibt, um äquivalenten Code zu schreiben (der expliziter ist), ist das Verbot der Syntax der geradlinigste Ansatz.

Spezifikation

Dies ist eine Änderung des Compilers, nicht der Grammatik. Der Compiler sollte das Folgende in einer finally Suite überprüfen

  • Ein return in jeder Anweisung, auf jeder Verschachtelungsebene.
  • Ein break/continue in jeder Anweisung, auf jeder Verschachtelungsebene, das den Kontrollfluss außerhalb der finally Suite übertragen würde.

Wenn ein solcher Fall gefunden wird, sollte er die entsprechende Ausnahme ausgeben

  • Für continue eine SyntaxError (dies ist das aktuelle Verhalten von 3.7).
  • Für return/break eine SyntaxWarning in 3.10 und danach eine SyntaxError.

Zum Beispiel sind die folgenden alle durch diesen Vorschlag verboten

def f():
    try:
        pass
    finally:
        return

def g():
    try:
        pass
    finally:
        try:
            return
        finally:
            pass

def h():
    try:
        pass
    finally:
        try:
            pass
        finally:
            for x in range(10):
                return

Das Folgende ist immer noch erlaubt, da das continue die finally nicht verlässt

try:
    pass
finally:
    for x in range(10):
        continue

Beachten Sie, dass das Yielden aus einem finally durch diese PEP akzeptabel bleibt, da das Fortsetzen des Generators das finally fortsetzen und schließlich alle aktiven Ausnahmen auslösen wird (sie werden also niemals durch Yielding unterdrückt).

Abwärtskompatibilität

Dies ist eine rückwärts inkompatible Änderung für return und break.

Die folgenden Stellen in der CPython-Standardbibliothek (bei v3.8.0b1-651-g7fcc2088a5) verwenden return innerhalb von finally

  • Lib/subprocess.py:921 - die Verwendung hier sieht wie ein Fehler aus
  • Lib/multiprocessing/connection.py:316 - die Verwendung hier sieht legitim aus, aber die Absicht ist nicht klar
  • Lib/multiprocessing/connection.py:318 - die Verwendung hier sieht legitim aus, aber die Absicht ist nicht klar
  • Lib/test/test_sys_settrace.py:837 - ein Test für return innerhalb von finally
  • Lib/test/test_sys_settrace.py:1346 - ein Test für return innerhalb von finally

Es gibt keine Verwendungen von break innerhalb eines finally (das aus dem finally ausbricht) in der Standardbibliothek.

Sicherheitsimplikationen

Dies ist eine Vereinfachung der Sprache und die Entfernung damit verbundener Code, daher sollte sie keine neuen Wege für Sicherheitslücken einführen.

Wie man das lehrt

Dieses Feature wird sehr selten verwendet, daher wird das Verbot wahrscheinlich nur fortgeschrittene Benutzer betreffen, nicht Anfänger und wahrscheinlich auch kein bestehendes Lehrmaterial. Da dies die Entfernung eines Features ist, werden Benutzer durch das Auslösen einer SyntaxError, falls das verbotene Feature verwendet wird, unterrichtet.

Referenzimplementierung

Es gibt derzeit keine Referenzimplementierung, obwohl die Art und Weise, wie continue derzeit in einem finally behandelt wird (Auslösen einer SyntaxError), auf return und break erweitert werden kann.

Referenzen


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

Zuletzt geändert: 2025-02-01 08:55:40 GMT