finally kann trickreich sein

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Schnell, ohne es im Interpreter auszuprobieren, was liefert dieses Programm:

Code: Alles auswählen

def a():
    while True:
        try: raise ValueError
        finally: break
a()
Wirft es eine Exception? Endet es überhaupt? Und warum ist es, wie es ist?

Stefan
Zuletzt geändert von sma am Sonntag 23. Januar 2011, 23:23, insgesamt 1-mal geändert.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Hm, also bei mir passierte genau das, was ich auch erwartete: RuntimeError weil das Rekursionslimit erreicht wurde. Natürlich nachdem ich die Einrückung korrigiert habe ;)

Endlosschleife wird gestartet, finally wird ausgeführt, Endlosschleife wird beendet, a ruft sich selbst auf und startet Endlosschleife erneut, finally wird ausgeführt, Endlosschleife wird beendet, und so weiter und so fort

Was hast du denn erwartet und warum?


Edit: genau genommen passiert nicht viel, wenn die Funktion nur definiert wird, aber nicht aufgerufen :twisted:
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Oh, jetzt verstehe ich, was du meinst. finally-break schein ein Spezialfall zu sein:

Code: Alles auswählen

>>> while True:
...     try:
...         raise Exception()
...     finally:
...         print 'boh'
... 
boh
Traceback (most recent call last):
  File "<input>", line 3, in <module>
Exception
>>> while True:
...     try:
...         raise Exception()
...     finally:
...         break
... 
>>> 
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Ich habe es mal ausprobiert (Python 2.6).

Das Programm beendet sich sehr schnell ohne Fehlermeldung. Ich würde einen ValueError erwarten, aber der wird anscheinend irgendwie von der Schleife oder dem break gefressen.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

sma hat geschrieben:Und warum ist es, wie es ist?
Vermutlich weil der finally-Block sicherstellen soll, dass der Code in ihm in jedem Fall ausgeführt wird. Das ginge bei return, raise oder break schlecht, wenn die Ausnahme nicht abgefangen würde.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Ist konsistent mit "die zwischengespeicherte Exception wird wieder geraised, wenn das Ende des finally-Blocks erreicht wird" - das Ende des finally-Blocks wird eben nicht erreicht.

Lustig auch (ungetestet):

Code: Alles auswählen

def a():
    try:
        return
    finally:
        a()
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

/me hat geschrieben:Ich habe es mal ausprobiert (Python 2.6).
Hey, ausprobieren ist schummeln :) Wenn man's ausprobiert hat, kann man sich's erklären, finde ich, aber ich hätte das auf den ersten Blick nicht vermutet.

Stefan
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

@bords0: Sorry, hatte die Einrückung verpatzt. Die von dir vermutete Endlosrekursion durch Aufruf von a() in finally wollte ich nicht konstruieren. Das a() sollte die Funktion einfach nur aufrufen, damit überhaupt etwas passiert.

Stefan
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Missverständnis :-)

Ich hatte nur darauf hinweisen wollen, dass finally selbst bei "return" im "try:" ausgeführt wird.

Für die Endlosschleife darf bei dir das "a()" natürlich nicht im finally-Zweig (d.h. hinter dem break) stehen, sondern auf Funktionsebene (so habe ich auch derdon verstanden).
lunar

Language Reference, Abschnitt 7.4 The try statement:
If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception, it is re-raised at the end of the finally clause. If the finally clause raises another exception or executes a return or break statement, the saved exception is lost. The exception information is not available to the program during execution of the finally clause.

When a return, break or continue statement is executed in the try suite of a try...finally statement, the finally clause is also executed ‘on the way out.’ A continue statement is illegal in the finally clause. (The reason is a problem with the current implementation — this restriction may be lifted in the future).
Irgendwie muss man "break" und "return" innerhalb eines "finally"-Blocks ja sinnvolle Semantik verleihen.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Bin ich eigentlich der einzige der dieses Verhalten absolut logisch und einfach nachvollziehbar findet? Das einzige was wirklich seltsam ist, ist dass `continue` im `finally` Block nicht erlaubt ist.

Wenn man `return`, `break` und `raise` im `finally` Block erlaubt müssen die, da als letztes ausgeführt, Priorität über die Anweisungen im `try` Block haben, alles andere wäre seltsam es sei den man führt den `finally` Block nicht aus. Letzteres mag sinnvoll sein ist allerdings definitiv unpraktisch.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

DasIch hat geschrieben:Bin ich eigentlich der einzige der dieses Verhalten absolut logisch und einfach nachvollziehbar findet?
Nein, der Meinung sind hier wohl alle.
Antworten