Beim Schreiben von Code möchte ich oft Folgendes tun:
try:
foo()
except FooError:
handle_foo()
else:
try:
bar()
except BarError:
handle_bar()
else:
try:
baz()
except BazError:
handle_baz()
else:
qux()
finally:
cleanup()
Dies ist offensichtlich völlig unlesbar. Es drückt jedoch eine relativ einfache Idee aus: Führen Sie eine Reihe von Funktionen (oder Kurzcode-Schnipsel) mit jeweils einem Ausnahmebehandler aus und stoppen Sie, sobald eine Funktion ausfällt. Ich stelle mir vor, Python könnte syntaktischen Zucker für diesen Code bereitstellen, vielleicht so etwas:
# NB: This is *not* valid Python
try:
foo()
except FooError:
handle_foo()
# GOTO finally block
else try:
bar()
except BarError:
handle_bar()
# ditto
else try:
baz()
except BazError:
handle_baz()
# ditto
else:
qux()
finally:
cleanup()
Wenn keine Ausnahmen gemacht werden, entspricht dies foo();bar();baz();qux();cleanup()
. Wenn Ausnahmen ausgelöst werden, werden sie vom entsprechenden Ausnahmebehandler (falls vorhanden) behandelt, und wir fahren mit fort cleanup()
. Insbesondere wenn bar()
ein FooError
oder BazError
ausgelöst wird, wird die Ausnahme nicht abgefangen und an den Anrufer weitergegeben. Dies ist wünschenswert, damit wir nur Ausnahmen abfangen, die wir wirklich erwarten.
Ist diese Art von Code unabhängig von der syntaktischen Hässlichkeit im Allgemeinen nur eine schlechte Idee? Wenn ja, wie würden Sie es umgestalten? Ich stelle mir vor, dass Kontextmanager verwendet werden könnten, um einen Teil der Komplexität zu absorbieren, aber ich verstehe nicht wirklich, wie das im allgemeinen Fall funktionieren würde.
quelle
handle_*
Funktionen?Antworten:
Was macht
handle_foo
das Es gibt einige Dinge, die wir normalerweise in Ausnahmebehandlungsblöcken tun.with
Es scheint mir, dass Sie bei der Ausnahmebehandlung etwas Seltsames tun. Ihre Frage hier ist einfach ein Symptom für die ungewöhnliche Verwendung von Ausnahmen. Sie fallen nicht in das typische Muster, und deshalb ist dies unangenehm geworden.
Ohne eine bessere Vorstellung davon, was Sie in diesen
handle_
Funktionen tun , ist das alles, was ich sagen kann.quelle
handle_
Funktionen können genauso gut kurze Codeausschnitte sein, die (sagen wir) den Fehler protokollieren und Fallback-Werte zurückgeben, neue Ausnahmen auslösen oder eine Reihe anderer Dinge tun. Inzwischen hat diefoo()
,bar()
könnte etc. Funktionen auch kurze Code - Schnipsel sein. Zum Beispiel könntenspam[eggs]
und müssen wir fangenKeyError
.return
oderraise
tut, würde das von Ihnen erwähnte Problem nicht auftreten. Der Rest der Funktion wird automatisch übersprungen. Eine einfache Möglichkeit, Ihr Problem zu beheben, besteht darin, in allen Ausnahmeblöcken zurückzukehren. Der Code ist umständlich, weil Sie keine übliche Kaution hinterlegen oder erhöhen, um anzuzeigen, dass Sie aufgeben.foo
die eine eigene Bereinigung durchführen sollten?foo
signalisiert, dass ein Fehler aufgetreten ist, und weiß nicht, wie das richtige Bereinigungsverfahren aussehen soll.Es scheint, als hätten Sie eine Folge von Befehlen, die möglicherweise eine Ausnahme auslösen, die vor der Rückkehr behandelt werden muss. Versuchen Sie, Ihren Code und die Ausnahmebehandlung an verschiedenen Orten zu gruppieren. Ich glaube, das macht, was Sie beabsichtigen.
quelle
try:
Block eingefügt wird. In diesem Fall wäre ich besorgt über dasbar()
Auslösen von aFooError
, was mit diesem Code falsch behandelt würde.Es gibt verschiedene Möglichkeiten, je nachdem, was Sie benötigen.
Hier ist ein Weg mit Schleifen:
Hier ist ein Weg mit einem Exit nach dem error_handler:
Persönlich denke ich, dass die Loop-Version leichter zu lesen ist.
quelle
Erstens kann eine angemessene Verwendung von
with
häufig einen Großteil des Codes für die Ausnahmebehandlung reduzieren oder sogar eliminieren, wodurch sowohl die Wartbarkeit als auch die Lesbarkeit verbessert werden.Jetzt können Sie die Verschachtelung auf viele Arten reduzieren. Andere Poster haben bereits einige bereitgestellt. Hier ist meine eigene Variante:
quelle