Warum brauchen wir die "finally" -Klausel in Python?


306

Ich bin mir nicht sicher, warum wir finallyin try...except...finallyAussagen brauchen . Meiner Meinung nach dieser Codeblock

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

ist das gleiche mit diesem mit finally:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

Vermisse ich etwas

Antworten:


422

Es macht einen Unterschied, wenn Sie früh zurückkehren:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

Vergleichen Sie damit:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

Andere Situationen, die Unterschiede verursachen können:

  • Wenn eine Ausnahme innerhalb des Ausnahmeblocks ausgelöst wird.
  • Wenn eine Ausnahme ausgelöst wird, dies run_code1()jedoch nicht der Fall ist TypeError.
  • Andere Kontrollflussanweisungen wie continueund breakAnweisungen.

1
versuche: #x = Hallo + 20 x = 10 + 20 außer: print 'Ich bin in außer Block' x = 20 + 30 sonst: print 'Ich bin in sonst Block' x + = 1 endlich: print 'Endlich x =% s '% (x)
Abhijit Sahu

89

Sie können finallydamit sicherstellen, dass Dateien oder Ressourcen geschlossen oder freigegeben werden, unabhängig davon, ob eine Ausnahme auftritt, auch wenn Sie die Ausnahme nicht abfangen. (Oder wenn Sie diese spezielle Ausnahme nicht abfangen .)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

In diesem Beispiel ist es besser, die withAnweisung zu verwenden, aber diese Art von Struktur kann für andere Arten von Ressourcen verwendet werden.

Ein paar Jahre später schrieb ich einen Blog-Beitrag über einen Missbrauch finally, den die Leser vielleicht amüsant finden.


23

Sie sind nicht gleichwertig. Schließlich wird Code ausgeführt, egal was sonst noch passiert. Dies ist nützlich für Bereinigungscode, der ausgeführt werden muss.


15
Finally code is run no matter what else happens... es sei denn, es gibt eine Endlosschleife. Oder ein Stromausfall. Oder os._exit(). Oder ...
Mark Byers

3
@Mark Tatsächlich löst sys.exit eine normale Ausnahme aus. Aber ja, alles, was dazu führt, dass der Prozess sofort beendet wird, bedeutet, dass nichts anderes ausgeführt wird.
Antimon

1
@ Antimon: Danke. Geändert zu os._exit.
Mark Byers

Ich frage mich nur, warum der Bereinigungscode nicht in die Ausnahme eingefügt werden kann, wenn der Code nur dann eingegeben wird, wenn eine Ausnahme gefunden wird.
Stephen Jacob

2
@Stephen Zum einen wird der Code schließlich auch dann ausgeführt, wenn Sie vom try-Block zurückkehren. In diesem Fall werden Sie die Ausnahmeklausel nicht treffen.
Antimon

18

Um die anderen obigen Antworten zu ergänzen, wird die finallyKlausel unabhängig davon ausgeführt, während die elseKlausel nur ausgeführt wird, wenn keine Ausnahme ausgelöst wurde.

Wenn Sie beispielsweise ohne Ausnahmen in eine Datei schreiben, wird Folgendes ausgegeben:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

AUSGABE:

Writing to file.
Write successful.
File closed.

Wenn es eine Ausnahme gibt, gibt der Code Folgendes aus (beachten Sie, dass ein absichtlicher Fehler dadurch verursacht wird, dass die Datei schreibgeschützt bleibt.

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

AUSGABE:

Could not write to file.
File closed.

Wir können sehen, dass die finallyKlausel unabhängig von einer Ausnahme ausgeführt wird. Hoffe das hilft.


2
Dies hätte funktioniert, selbst wenn Sie nicht die "finally" -Klausel verwendet hätten, die die Frage nicht beantwortet, da OP den Unterschied wissen möchte. Ein gutes Beispiel hätte einen anderen Fehler als IOError verursacht, um zu zeigen, dass die Der Klauselblock finally wird ausgeführt, bevor die Ausnahme an den Aufrufer weitergegeben wird.
Reda Drissi

2
Ich wusste nicht, dass elsees eine Sache ist. Nützlich zu wissen.
Mazunki

8

Die Codeblöcke sind nicht äquivalent. Die finallyKlausel wird auch ausgeführt, wenn run_code1()eine andere Ausnahme als TypeErroroder run_code2()eine Ausnahme ausgelöst wird, während sie other_code()in den ersten Fällen in diesen Fällen nicht ausgeführt wird.


7

Was passiert in Ihrem ersten Beispiel, wenn run_code1()eine Ausnahme ausgelöst wird, die dies nicht ist TypeError? ... other_code()wird nicht ausgeführt.

Vergleichen Sie das mit der finally:Version: other_code()wird garantiert ausgeführt, unabhängig davon, welche Ausnahme ausgelöst wird.


7

Wie in der Dokumentation erläutert , soll die finallyKlausel Bereinigungsaktionen definieren, die unter allen Umständen ausgeführt werden müssen .

Wenn finallyvorhanden, wird ein Bereinigungshandler angegeben. Die try Klausel wird ausgeführt, einschließlich aller Klauseln exceptund else. Wenn eine Ausnahme in einer der Klauseln auftritt und nicht behandelt wird, wird die Ausnahme vorübergehend gespeichert. Die finallyKlausel wird ausgeführt. Wenn es eine gespeicherte Ausnahme gibt, wird diese am Ende der finally Klausel erneut ausgelöst.

Ein Beispiel:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Wie Sie sehen, wird die finallyKlausel auf jeden Fall ausgeführt. Das TypeErrorErhöhen durch Teilen zweier Zeichenfolgen wird von der exceptKlausel nicht behandelt und daher nach finallyAusführung der Klausel erneut ausgelöst.

In realen Anwendungen ist die finally-Klausel nützlich, um externe Ressourcen (wie Dateien oder Netzwerkverbindungen) freizugeben, unabhängig davon, ob die Verwendung der Ressource erfolgreich war.


4

Perfektes Beispiel ist wie folgt:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)

3

finallydient zum Definieren von "Bereinigungsaktionen" . Die finallyKlausel wird in jedem Fall ausgeführt, bevor die tryAnweisung verlassen wird, unabhängig davon, ob eine Ausnahme (auch wenn Sie sie nicht behandeln) aufgetreten ist oder nicht.

Ich stimme dem Beispiel von @ Byers zu.


2

Kann schließlich auch verwendet werden, wenn Sie "optionalen" Code ausführen möchten, bevor Sie den Code für Ihre Hauptarbeit ausführen, und dieser optionale Code aus verschiedenen Gründen fehlschlagen kann.

Im folgenden Beispiel wissen wir nicht genau, welche Ausnahmen auftreten store_some_debug_infokönnen.

Wir könnten rennen:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

Die meisten Linters werden sich jedoch darüber beschweren, dass eine Ausnahme zu vage ist. Da wir uns nur passfür Fehler entscheiden, bietet der exceptBlock keinen wirklichen Mehrwert.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

Der obige Code hat den gleichen Effekt wie der 1. Codeblock, ist jedoch präziser.


2

Durch die jahrelange professionelle Verwendung von Delphi habe ich gelernt, meine Bereinigungsroutinen endgültig zu schützen. Delphi erzwingt so ziemlich die Verwendung von final, um alle vor dem try-Block erstellten Ressourcen zu bereinigen, damit Sie keinen Speicherverlust verursachen. So funktionieren auch Java, Python und Ruby.

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

und die Ressource wird bereinigt, unabhängig davon, was Sie zwischen dem Versuch und dem endgültigen Vorgang tun. Außerdem wird es nicht bereinigt, wenn die Ausführung den tryBlock nie erreicht . (dh create_resourceselbst löst eine Ausnahme aus) Es macht Ihren Code "Ausnahme sicher".

Warum Sie eigentlich einen finally-Block benötigen, tun dies nicht alle Sprachen. In C ++, wo Sie automatisch Destruktoren aufgerufen haben, die die Bereinigung erzwingen, wenn eine Ausnahme den Stapel abrollt. Ich denke, dies ist ein Schritt in Richtung sauberer Code im Vergleich zu versuchen ... endlich Sprachen.

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.

2

Ein try-Block hat nur eine obligatorische Klausel: Die try-Anweisung. Die Ausnahmeregelungen else, else und finally sind optional und basieren auf den Benutzereinstellungen.

finally: Bevor Python die try-Anweisung verlässt, wird der Code im finally-Block unter allen Bedingungen ausgeführt, auch wenn das Programm beendet wird. Wenn Python beispielsweise beim Ausführen von Code im Block Except oder else auf einen Fehler gestoßen ist, wird der Block finally weiterhin ausgeführt, bevor das Programm gestoppt wird.


1
Das ist falsch. Die Ausnahme-Anweisung ist obligatorisch. - Lucas Azevedo 1. Februar um 12:04 Dies ist falsch, da ich gerade ein Python 3.5-Programm mit einem Try-finally-Block ohne "Ausnahme" -Klausel kompiliert und ausgeführt habe.
Rob Tow

2
Ich habe es selbst versucht und zu meinem Unglauben ist die Ausnahmeklausel nicht obligatorisch.
Captainblack

1

Führen Sie diese Python3-Codes aus, um festzustellen, ob Folgendes erforderlich ist:

FALL 1:

count = 0
while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")
        finally:
            print("Your Attempts: {}".format(count))

FALL 2:

count = 0

while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")

        print("Your Attempts: {}".format(count))

Versuchen Sie jedes Mal die folgenden Eingaben:

  1. zufällige ganze Zahlen
  2. korrekter Code, der 586 ist (Versuchen Sie dies und Sie erhalten Ihre Antwort)
  3. zufällige Zeichenfolgen

** In einem sehr frühen Stadium des Lernens von Python.


1

Ich habe versucht, einen Code auszuführen, in dem ich Excel-Tabellen lesen wollte. Das Problem war, wenn es eine Datei gibt, die kein Blatt mit dem Namen "SheetSum" enthält. Ich kann sie nicht an den Fehlerort verschieben. Code, den ich schrieb, war:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Fehler geben:

[WinError 32] Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird

Ich musste einen vollständigen try except with finallyBlock hinzufügen und sagen, dass finallyich die Datei auf jeden Fall schließen muss, wie:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Ansonsten bleibt die Datei im Hintergrund offen.

Wenn finallyvorhanden, wird ein Bereinigungshandler angegeben . Die try Klausel wird ausgeführt, einschließlich aller Klauseln exceptund else. Wenn eine Ausnahme in einer der Klauseln auftritt und nicht behandelt wird, wird die Ausnahme vorübergehend gespeichert . Die finallyKlausel wird ausgeführt. Wenn es eine gespeicherte Ausnahme gibt, wird diese am Ende der finally Klausel erneut ausgelöst. Wenn die finallyKlausel eine andere Ausnahme auslöst, wird die gespeicherte Ausnahme als Kontext der neuen Ausnahme festgelegt.

..Mehr hier

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.