Ist für jeden möglichen Try-finally-Block in Python garantiert, dass der finally
Block immer ausgeführt wird?
Nehmen wir zum Beispiel an, ich kehre in einem except
Block zurück:
try:
1/0
except ZeroDivisionError:
return
finally:
print("Does this code run?")
Oder vielleicht erhebe ich noch einmal Exception
:
try:
1/0
except ZeroDivisionError:
raise
finally:
print("What about this code?")
Tests zeigen, dass finally
dies für die obigen Beispiele ausgeführt wird, aber ich stelle mir vor, dass es andere Szenarien gibt, an die ich nicht gedacht habe.
Gibt es Szenarien, in denen ein finally
Block in Python nicht ausgeführt werden kann?
python
exception-handling
try-catch-finally
finally
Stevoisiak
quelle
quelle
finally
nicht auszuführen oder "seinen Zweck zu vereiteln", ist während einer Endlosschleifesys.exit
oder eines erzwungenen Interrupts. Die Dokumentation besagt, dassfinally
immer ausgeführt wird, also würde ich damit weitermachen.finally
es nicht ausgeführt wird , wenn Sie den Task-Manager öffnen und den Prozess beenden. Oder das gleiche, wenn der Computer vorher abstürzt: Dfinally
wird nicht ausgeführt, wenn das Netzkabel von der Wand gerissen wird.Antworten:
"Garantiert" ist ein viel stärkeres Wort als jede Umsetzung von
finally
verdient. Was garantiert ist , dass , wenn die Ausführung fließt aus dem Ganzentry
-finally
Konstrukt, wird es durch den Pass ,finally
dies zu tun. Was nicht garantiert ist, ist, dass die Ausführung aus demtry
- heraus fließtfinally
.A
finally
in einem Generator oder einer asynchronen Coroutine wird möglicherweise nie ausgeführt , wenn das Objekt niemals zum Abschluss ausgeführt wird. Es gibt viele Möglichkeiten, die passieren können. hier ist eine:Beachten Sie, dass dieses Beispiel etwas knifflig ist: Wenn der Generator durch Müll gesammelt wird, versucht Python, den
finally
Block auszuführen , indem eineGeneratorExit
Ausnahme ausgelöst wird. Hier wird jedoch diese Ausnahme abgefangen, undyield
an diesem Punkt gibt Python eine Warnung aus ("Generator ignoriert GeneratorExit" ") und gibt auf. Weitere Informationen finden Sie in PEP 342 (Coroutinen über erweiterte Generatoren) .Andere Möglichkeiten , wie ein Generator oder Koroutine möglicherweise nicht zu dem Schluss führen Sie schließen , wenn das Objekt wird einfach nie GC'ed (ja, das möglich ist, auch in CPython), oder wenn ein
async with
await
s in__aexit__
, oder wenn das Objektawait
s oderyield
s in einemfinally
Block. Diese Liste erhebt keinen Anspruch auf Vollständigkeit.Ein
finally
Thread in einem Daemon wird möglicherweise nie ausgeführt, wenn alle Nicht-Daemon-Threads zuerst beendet werden.os._exit
stoppt den Prozess sofort, ohnefinally
Blöcke auszuführen .os.fork
kann zufinally
Blöcken auszuführen zweimal . Neben den normalen Problemen, die Sie von zweimaligen Ereignissen erwarten, kann dies zu gleichzeitigen Zugriffskonflikten (Abstürze, Blockierungen usw.) führen, wenn der Zugriff auf freigegebene Ressourcen nicht korrekt synchronisiert ist .Da
multiprocessing
Anwendungen gabel ohne-exec Arbeitsprozesse zu erstellen , wenn die Verwendung von Gabelstartmethode (der Standard unter Unix), und ruft dann in den Arbeitern einmal die Aufgabe der Arbeiter durchgeführt wird, und kann Interaktion problematisch sein ( Beispiel ).os._exit
finally
multiprocessing
finally
, dass Blöcke ausgeführt werden.kill -SIGKILL
verhindertfinally
, dass Blöcke ausgeführt werden.SIGTERM
undSIGHUP
verhindert auchfinally
Blöcke von Lauf , wenn Sie einen Handler installieren Sie das Herunterfahren selbst zu steuern; Standardmäßig verarbeitet Python nichtSIGTERM
oderSIGHUP
.finally
kann verhindern, dass die Bereinigung abgeschlossen wird. Ein besonders bemerkenswerter Fall ist, wenn der Benutzer Control-C drückt, gerade als wir mit der Ausführung desfinally
Blocks beginnen. Python löst einKeyboardInterrupt
und überspringt jede Zeile desfinally
Blockinhalts. (KeyboardInterrupt
-sicherer Code ist sehr schwer zu schreiben).finally
werden keine Blöcke ausgeführt.Der
finally
Block ist kein Transaktionssystem. Es bietet keine Atomgarantien oder ähnliches. Einige dieser Beispiele mögen offensichtlich erscheinen, aber es ist leicht zu vergessen, dass solche Dinge passieren können und sichfinally
zu sehr darauf verlassen können.quelle
except
und fangen Sie niemalsGeneratorExit
in einem Generator. Die Punkte über Threads / Beenden des Prozesses / Segfaulting / Ausschalten werden erwartet, Python kann keine Magie ausführen. Auch: Ausnahmen infinally
sind offensichtlich ein Problem , aber das macht nichts an der Tatsache , dass der Steuerfluss wurde auf dem bewegtenfinally
Block. In Bezug aufCtrl+C
, können Sie einen Signal - Handler , dass sie ignoriert oder einfach „ Zeitplan“ eine saubere Abschaltung nach dem aktuellen Vorgang abgeschlossen ist hinzuzufügen.kill -9
hat keine Sprache angegeben. Und ehrlich gesagt muss es wiederholt werden, weil es an einem blinden Fleck sitzt. Zu viele Leute vergessen oder merken nicht, dass ihr Programm auf der Strecke bleiben könnte, ohne dass sie überhaupt aufräumen dürfen.finally
Blöcke verlassen, als ob sie Transaktionsgarantien geben würden. Es mag offensichtlich erscheinen, dass dies nicht der Fall ist, aber es ist nicht jedermanns Sache. Was den Generatorfall betrifft, gibt es viele Möglichkeiten, wie ein Generator überhaupt nicht GC-fähig sein kann, und viele Möglichkeiten, wie ein Generator oder eine Coroutine versehentlich nachgeben kann,GeneratorExit
selbst wenn er die nicht erfasstGeneratorExit
, z. B. wenn einasync with
Suspendiert eine Coroutine in__exit__
.Ja. Endlich gewinnt immer.
Die einzige Möglichkeit, dies zu verhindern, besteht darin, die Ausführung anzuhalten, bevor
finally:
die Ausführung möglich ist (z. B. den Interpreter zum Absturz bringen, den Computer ausschalten, einen Generator für immer anhalten ).Hier sind noch ein paar, an die Sie vielleicht nicht gedacht haben:
Abhängig davon, wie Sie den Interpreter verlassen, können Sie manchmal endgültig "abbrechen", aber nicht so:
Verwendung der prekären
os._exit
(dies fällt meiner Meinung nach unter "Absturz des Dolmetschers"):Ich führe derzeit diesen Code aus, um zu testen, ob er nach dem Hitzetod des Universums endlich noch ausgeführt wird:
Ich warte jedoch immer noch auf das Ergebnis. Schauen Sie später noch einmal hier vorbei.
quelle
finally
In einem Generator oder einer Coroutine kann die Ausführung leicht fehlschlagen , ohne dass die Bedingung "Absturz des Dolmetschers" erreicht wird.sleep(1)
was definitiv zu undefiniertem Verhalten führen würde. :-Dos._exit
ist praktisch das Gleiche wie das Auslösen eines Absturzes (unreiner Exit). Der richtige Weg zum Verlassen istsys.exit
.Laut der Python-Dokumentation :
Es sollte auch beachtet werden, dass, wenn mehrere return-Anweisungen vorhanden sind, einschließlich einer im finally-Block, die finally-Block-Rückgabe die einzige ist, die ausgeführt wird.
quelle
Ja und nein.
Was garantiert ist, ist, dass Python immer versucht, den finally-Block auszuführen. Wenn Sie vom Block zurückkehren oder eine nicht erfasste Ausnahme auslösen, wird der finally-Block unmittelbar vor dem tatsächlichen Zurückgeben oder Auslösen der Ausnahme ausgeführt.
(Was Sie selbst hätten kontrollieren können, indem Sie einfach den Code in Ihrer Frage ausgeführt hätten)
Der einzige Fall, in dem ich mir vorstellen kann, wo der finally-Block nicht ausgeführt wird, ist, wenn der Python-Interpreter selbst abstürzt, beispielsweise innerhalb von C-Code oder aufgrund eines Stromausfalls.
quelle
Ich habe diese gefunden, ohne eine Generatorfunktion zu verwenden:
Der Ruhezustand kann ein beliebiger Code sein, der möglicherweise inkonsistent ausgeführt wird.
Was hier zu passieren scheint, ist, dass der erste parallele Prozess, der beendet wird, den try-Block erfolgreich verlässt, dann aber versucht, von der Funktion einen Wert (foo) zurückzugeben, der nirgendwo definiert wurde, was eine Ausnahme verursacht. Diese Ausnahme beendet die Karte, ohne dass die anderen Prozesse ihre endgültigen Blöcke erreichen können.
Wenn Sie die Zeile
bar = bazz
direkt nach dem Aufruf von sleep () im try-Block hinzufügen . Dann löst der erste Prozess, der diese Linie erreicht, eine Ausnahme aus (da bazz nicht definiert ist), die bewirkt, dass sein eigener finally-Block ausgeführt wird, aber dann die Karte beendet, wodurch die anderen try-Blöcke verschwinden, ohne ihre finally-Blöcke zu erreichen, und der erste Prozess, der auch seine return-Anweisung nicht erreicht.Für die Python-Mehrfachverarbeitung bedeutet dies, dass Sie dem Mechanismus zur Ausnahmebehandlung nicht vertrauen können, um Ressourcen in allen Prozessen zu bereinigen, wenn auch nur einer der Prozesse eine Ausnahme haben kann. Zusätzliche Signalverarbeitung oder Verwaltung der Ressourcen außerhalb des Multiprocessing-Map-Aufrufs wäre erforderlich.
quelle
Nachtrag zur akzeptierten Antwort, nur um zu sehen, wie es funktioniert, mit ein paar Beispielen:
Dies:
wird ausgegeben
wird ausgegeben
quelle