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

Python Enhancement Proposals

PEP 310 – Zuverlässige Erfassungs-/Freigabepaare

Autor:
Michael Hudson <mwh at python.net>, Paul Moore <p.f.moore at gmail.com>
Status:
Abgelehnt
Typ:
Standards Track
Erstellt:
18. Dez 2002
Python-Version:
2.4
Post-History:


Inhaltsverzeichnis

Zusammenfassung

Es wäre schön, eine weniger tippe-intensive Möglichkeit zu haben, Folgendes zu schreiben:

the_lock.acquire()
try:
    ....
finally:
    the_lock.release()

Dieser PEP schlägt ein Stück Syntax (einen „with“-Block) und eine „kleine“ Schnittstelle vor, die das obige verallgemeinert.

Bekanntmachung

Dieser PEP wird zugunsten von PEP 343 abgelehnt.

Begründung

Einer der Vorteile der Ausnahmebehandlungsphilosophie von Python ist, dass es schwieriger macht, das „Falsche“ zu tun (z. B. den Rückgabewert eines Systemaufrufs nicht zu überprüfen). Derzeit gilt dies nicht für die Ressourcenbereinigung. Die aktuelle Syntax für die Erfassung und Freigabe einer Ressource (z. B. eines Schlosses) lautet:

the_lock.acquire()
try:
    ....
finally:
    the_lock.release()

Diese Syntax trennt die Erfassung und die Freigabe durch einen (möglicherweise großen) Codeblock, was es schwierig macht, „auf einen Blick“ zu bestätigen, dass der Code die Ressource korrekt verwaltet. Ein weiterer häufiger Fehler ist die Codierung des „acquire“-Aufrufs innerhalb des try-Blocks, wodurch das Schloss bei einem Fehler beim Erwerb falsch freigegeben wird.

Grundlegende Syntax und Semantik

Die Syntax einer ‚with‘-Anweisung lautet wie folgt:

'with' [ var '=' ] expr ':'
    suite

Diese Anweisung wird als äquivalent zu der folgenden Sequenz von Anweisungen definiert:

var = expr

if hasattr(var, "__enter__"):
    var.__enter__()

try:
    suite

finally:
    var.__exit__()

(Das Vorhandensein einer `__exit__`-Methode wird *nicht* wie bei `__enter__` überprüft, um sicherzustellen, dass die Verwendung ungeeigneter Objekte in with:-Anweisungen einen Fehler verursacht).

Wenn die Variable weggelassen wird, wird ein unbenanntes Objekt auf dem Stapel zugewiesen. In diesem Fall hat die Suite keinen Zugriff auf das unbenannte Objekt.

Mögliche Erweiterungen

Eine Reihe potenzieller Erweiterungen der Basissyntax wurden auf der Python Developers-Liste diskutiert. Keine dieser Erweiterungen ist in der von diesem PEP vorgeschlagenen Lösung enthalten. In vielen Fällen sind die Argumente in beide Richtungen fast gleich stark. In solchen Fällen hat der PEP immer Einfachheit gewählt, einfach weil dort, wo zusätzliche Leistung benötigt wird, der vorhandene try-Block verfügbar ist.

Mehrere Ausdrücke

Ein Vorschlag war, mehrere Ausdrücke innerhalb einer ‚with‘-Anweisung zuzulassen. Die `__enter__`-Methoden würden von links nach rechts und die `__exit__`-Methoden von rechts nach links aufgerufen. Der Vorteil wäre, dass verschachtelte ‚with‘-Anweisungen, wo mehr als eine Ressource verwaltet wird, dazu führen, dass der Code nach rechts abdriftet. Die Lösung für dieses Problem ist die gleiche wie für jede andere tiefe Verschachtelung – einige des Codes in eine separate Funktion auslagern. Darüber hinaus muss die Frage, was passiert, wenn eine der `__exit__`-Methoden eine Ausnahme auslöst (sollen die anderen `__exit__`-Methoden aufgerufen werden?), geklärt werden.

Ausnahmebehandlung

Eine Erweiterung des Protokolls um einen optionalen `__except__`-Handler, der aufgerufen wird, wenn eine Ausnahme ausgelöst wird und die Ausnahme behandeln oder erneut auslösen kann, wurde vorgeschlagen. Es ist überhaupt nicht klar, ob die Semantik dieser Erweiterung präzise und verständlich gemacht werden kann. Zum Beispiel, sollte der äquivalente Code `try ... except ... else` sein, wenn ein Ausnahmehandler definiert ist, und `try ... finally`, wenn nicht? Wie kann dies im Allgemeinen zur Kompilierzeit bestimmt werden? Die Alternative ist, den Code so zu definieren, dass er sich zu einem `try ... except` innerhalb eines `try ... finally` erweitert. Aber das tut möglicherweise nicht das Richtige im wirklichen Leben.

Der einzige identifizierte Anwendungsfall für die Ausnahmebehandlung ist die transaktionale Verarbeitung (Commit bei sauberem Abschluss und Rollback bei einer Ausnahme). Dies ist wahrscheinlich mit einem konventionellen `try ... except ... else`-Block genauso einfach zu handhaben, und daher enthält der PEP keine Unterstützung für Ausnahmehandler.

Implementierungs-Hinweise

Es gibt eine potenzielle Race Condition im Code, der als äquivalent zur with-Anweisung spezifiziert ist. Wenn zum Beispiel eine `KeyboardInterrupt`-Ausnahme zwischen dem Abschluss des Aufrufs der `__enter__`-Methode und dem Beginn des try-Blocks ausgelöst wird, wird die `__exit__`-Methode nicht aufgerufen. Dies kann zu Ressourcenlecks oder Deadlocks führen. [XXX Guido hat erklärt, dass ihm diese Art von Race Condition wichtig ist und er vorhat, einige C-Magie dafür zu schreiben. Die Implementierung der ‚with‘-Anweisung sollte dies kopieren.]

Offene Fragen

Sollten bestehende Klassen (z. B. dateiähnliche Objekte und Sperren) geeignete `__enter__`- und `__exit__`-Methoden erhalten? Der offensichtliche Grund dafür ist die Bequemlichkeit (kein Adapter nötig). Das Argument dagegen ist, dass, wenn eingebaute Dateien diese haben, aber (sagen wir) `StringIO` nicht, dann kann Code, der „with“ auf einem Dateiobjekt verwendet, nicht mit einem `StringIO`-Objekt wiederverwendet werden. Also wird `__exit__ = close` Teil des „dateiähnlichen Objekt“-Protokolls, das benutzerdefinierte Klassen unterstützen müssen.

Der `__enter__`-Hook ist möglicherweise unnötig – für viele Anwendungsfälle wird eine Adapterklasse benötigt, und in diesem Fall kann die Arbeit, die vom `__enter__`-Hook geleistet wird, genauso einfach im `__init__`-Hook erledigt werden.

Wenn eine Möglichkeit zur expliziten Steuerung von Objektlebensdauern verfügbar wäre, könnte die Funktion des `__exit__`-Hooks vom vorhandenen `__del__`-Hook übernommen werden. Ein E-Mail-Austausch [1] mit einem Befürworter dieses Ansatzes hat einen der Autoren noch mehr davon überzeugt, dass es nicht die richtige Idee ist…

Es wurde vorgeschlagen [2], dass die Methode „__exit__“ „close“ genannt werden sollte oder dass eine „close“-Methode in Betracht gezogen werden sollte, wenn keine `__exit__`-Methode gefunden wird, um die „Out-of-the-Box-Nützlichkeit“ des „with …“-Konstrukts zu erhöhen.

Es gibt einige konzeptionelle Ähnlichkeiten zwischen ‚with …‘-Blöcken und Generatoren, die zu Vorschlägen geführt haben, dass for-Schleifen die Funktionalität von with-Blöcken implementieren könnten [3]. Obwohl auf einigen Ebenen raffiniert, denken wir, dass for-Schleifen Schleifen bleiben sollten.

Alternative Ideen

IEXEC: Holger Krekel – verallgemeinerter Ansatz mit XML-ähnlicher Syntax (keine URL gefunden…).

Holger hat weitreichendere Ideen zu „Execution Monitors“, die über Details des Kontrollflusses im überwachten Block informiert werden. Obwohl interessant, könnten diese Ideen die Sprache auf tiefgreifende und subtile Weise verändern und gehören daher zu einem anderen PEP.

Jede Smalltalk/Ruby-Erweiterung im Stil anonymer Blöcke subsumiert offensichtlich diese.

PEP 319 ist im selben Bereich, gewann aber bei der Vorstellung auf python-dev keine Unterstützung.

Abwärtskompatibilität

Dieser PEP schlägt ein neues Schlüsselwort vor, daher muss das `__future__`-Spiel gespielt werden.

Kosten der Übernahme

Diejenigen, die behaupten, die Sprache werde immer größer und komplizierter, haben etwas anderes zu beklagen. Es ist etwas anderes zu lehren.

Damit der Vorschlag nützlich ist, müssen viele dateiähnliche und sperrenähnliche Klassen in der Standardbibliothek und anderer Code Folgendes erhalten:

__exit__ = close

oder ähnliches hinzugefügt.

Kosten der Nicht-Übernahme

Das Schreiben korrekten Codes erfordert weiterhin mehr Aufwand als das Schreiben inkorrekten Codes.

Referenzen

Es gibt verschiedene Diskussionen in python-list und python-dev, die hier erwähnt werden könnten.


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

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