Perché abbiamo bisogno della clausola "finalmente" in Python?


306

Non sono sicuro del motivo per cui abbiamo bisogno finallydi try...except...finallydichiarazioni. Secondo me, questo blocco di codice

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

è lo stesso con questo usando finally:

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

Mi sto perdendo qualcosa?

Risposte:


422

Fa differenza se torni presto:

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

Confronta con questo:

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

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

Altre situazioni che possono causare differenze:

  • Se viene generata un'eccezione all'interno del blocco tranne.
  • Se viene generata un'eccezione run_code1()ma non è a TypeError.
  • Altre istruzioni del flusso di controllo come continuee breakdichiarazioni.

1
prova: #x = Ciao + 20 x = 10 + 20 tranne: stampa 'Sono dentro tranne il blocco' x = 20 + 30 altrimenti: stampa 'Sono nel blocco altro' x + = 1 finalmente: stampa 'Finalmente x =% s '% (x)
Abhijit Sahu,

89

È possibile utilizzare finallyper assicurarsi che i file o le risorse siano chiusi o rilasciati indipendentemente dal fatto che si verifichi un'eccezione, anche se non si rileva l'eccezione. (O se non si rileva quell'eccezione specifica .)

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 questo esempio sarebbe meglio usare l' withistruzione, ma questo tipo di struttura può essere utilizzato per altri tipi di risorse.

Qualche anno dopo, ho scritto un post sul blog su un abuso finallyche i lettori potrebbero trovare divertente.


23

Non sono equivalenti. Finalmente il codice viene eseguito, qualunque cosa accada. È utile per il codice di pulizia che deve essere eseguito.


15
Finally code is run no matter what else happens... a meno che non ci sia un ciclo infinito. O un powercut. Or os._exit(). O ...
Mark Byers, il

3
@Mark In realtà, sys.exit genera una normale eccezione. Sì, tutto ciò che provoca la chiusura immediata del processo significa che non viene eseguito nient'altro.
Antimonio,

1
@Antimonio: grazie. Modificato in os._exit.
Mark Byers,

Mi chiedo solo, perché non è possibile inserire il codice di pulizia all'interno tranne se il codice verrà inserito tranne se viene rilevata un'eccezione?
Stephen Jacob,

2
@Stephen Per prima cosa, finalmente il codice viene eseguito anche se si ritorna dal blocco try. In tal caso, non colpirai la clausola tranne.
Antimonio,

18

Per aggiungere alle altre risposte precedenti, la finallyclausola viene eseguita indipendentemente da ciò che la elseclausola esegue solo se non è stata sollevata un'eccezione.

Ad esempio, la scrittura su un file senza eccezioni genererà quanto segue:

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.")

PRODUZIONE:

Writing to file.
Write successful.
File closed.

Se esiste un'eccezione, il codice genererà quanto segue (si noti che un errore intenzionale viene causato mantenendo il file di sola lettura.

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.")

PRODUZIONE:

Could not write to file.
File closed.

Possiamo vedere che la finallyclausola viene eseguita indipendentemente da un'eccezione. Spero che questo ti aiuti.


2
Questo avrebbe funzionato anche se non avessi usato la clausola "finally" che non risponde alla domanda poiché OP vuole conoscere la differenza, un buon esempio avrebbe causato un errore diverso da IOError, per dimostrare che il infine il blocco di clausole viene eseguito prima che l'eccezione venga propagata al chiamante.
Reda Drissi,

2
Non sapevo elsefosse una cosa. Utile da sapere.
mazunki,

8

I blocchi di codice non sono equivalenti. La finallyclausola verrà eseguita anche se run_code1()genera un'eccezione diversa da TypeError, o se run_code2()genera un'eccezione, mentre other_code()nella prima versione non verrebbe eseguita in questi casi.


7

Nel tuo primo esempio, cosa succede se run_code1()solleva un'eccezione che non lo è TypeError? ... other_code()non verrà eseguito.

Confrontalo con la finally:versione: other_code()è garantito che venga eseguito indipendentemente da qualsiasi eccezione sollevata.


7

Come spiegato nella documentazione , la finallyclausola ha lo scopo di definire le azioni di pulizia che devono essere eseguite in tutte le circostanze .

Se finallypresente, specifica un gestore 'cleanup'. La try clausola viene eseguita, incluse eventuali clausole excepte else. Se si verifica un'eccezione in una qualsiasi delle clausole e non viene gestita, l'eccezione viene temporaneamente salvata. La finallyclausola viene eseguita. Se è presente un'eccezione salvata, viene nuovamente generata alla fine della finally clausola.

Un esempio:

>>> 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'

Come puoi vedere, la finallyclausola viene eseguita in ogni caso. Il rilancio TypeErrordividendo due stringhe non viene gestito dalla exceptclausola e quindi viene rilanciato dopo che la finallyclausola è stata eseguita.

Nelle applicazioni del mondo reale, la clausola finally è utile per rilasciare risorse esterne (come file o connessioni di rete), indipendentemente dal fatto che l'uso della risorsa abbia avuto esito positivo.


4

L'esempio perfetto è il seguente:

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

finallyserve per definire "azioni di pulizia" . La finallyclausola viene eseguita in ogni caso prima di lasciare l' tryistruzione, indipendentemente dal fatto che si sia verificata un'eccezione (anche se non la gestisci).

Secondo esempio di Byers.


2

Infine, può essere utilizzato anche quando si desidera eseguire il codice "opzionale" prima di eseguire il codice per il lavoro principale e tale codice opzionale potrebbe non riuscire per vari motivi.

Nel seguente esempio, non sappiamo esattamente quale tipo di eccezioni store_some_debug_infopotrebbe generare.

Potremmo correre:

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

Ma la maggior parte dei linters si lamenterà di aver colto un'eccezione troppo vaga. Inoltre, poiché scegliamo solo passerrori, il exceptblocco non aggiunge valore.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

Il codice sopra ha lo stesso effetto del primo blocco di codice ma è più conciso.


2

L'utilizzo professionale di Delphi per alcuni anni mi ha insegnato a salvaguardare le mie routine di pulizia usando finalmente. Delphi praticamente impone l'uso di finalmente per ripulire tutte le risorse create prima del blocco try, per non causare una perdita di memoria. Questo è anche il modo in cui funzionano Java, Python e Ruby.

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

e le risorse verranno ripulite indipendentemente da ciò che fai tra try e infine. Inoltre, non verrà pulito se l'esecuzione non raggiunge mai il tryblocco. (ovvero create_resourcegenera un'eccezione) Rende il tuo codice "sicuro".

Quanto al perché in realtà hai bisogno di un blocco finalmente, non tutte le lingue lo fanno. In C ++ dove hai chiamato automaticamente i distruttori che impongono la pulizia quando un'eccezione srotola lo stack. Penso che questo sia un passo avanti nella direzione di un codice più pulito rispetto a provare ... finalmente le lingue.

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

2

Un blocco try ha solo una clausola obbligatoria: l'istruzione try. Le clausole tranne, else e finally sono facoltative e basate sulle preferenze dell'utente.

finally: Prima che Python lasci l'istruzione try, eseguirà il codice nel blocco finally in qualsiasi condizione, anche se sta terminando il programma. Ad esempio, se Python ha riscontrato un errore durante l'esecuzione del codice nel blocco etc o else, il blocco finally verrà comunque eseguito prima di arrestare il programma.


1
Questo è sbagliato. La dichiarazione di eccezione è obbligatoria. - Lucas Azevedo, 1 febbraio alle 12:04 Questo è sbagliato, dato che ho appena compilato ed eseguito un programma Python 3.5 con un blocco try-finally, senza clausole "tranne".
Rob Tow,

2
L'ho provato io stesso e con mia incredulità, la clausola di eccezione non è obbligatoria.
captainblack,

1

Esegui questi codici Python3 per vedere finalmente la necessità di:

CASO 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))

Caso 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))

Prova ogni volta i seguenti input:

  1. numeri interi casuali
  2. codice corretto che è 586 (prova questo e otterrai la tua risposta)
  3. stringhe casuali

** In una fase molto precoce dell'apprendimento di Python.


1

Stavo cercando di eseguire un codice in cui volevo leggere fogli Excel. Il problema era, se c'è un file che non ha un foglio chiamato dire: Foglio non sono in grado di spostarlo nella posizione di errore !! Il codice che ho scritto era:

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')

Dare errore:

[WinError 32] Il processo non può accedere al file perché è utilizzato da un altro processo

Ho dovuto aggiungere il try except with finallyblocco completo e dire che finallyho bisogno di chiudere il file in ogni caso come:

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')

Altrimenti, il file rimane ancora aperto è lo sfondo.

Se finallypresente, specifica un gestore di pulizia . La try clausola viene eseguita, incluse eventuali clausole excepte else. Se si verifica un'eccezione in una qualsiasi delle clausole e non viene gestita, l' eccezione viene temporaneamente salvata . La finallyclausola viene eseguita. Se è presente un'eccezione salvata, viene nuovamente generata alla fine della finally clausola. Se lafinally clausola genera un'altra eccezione, l'eccezione salvata viene impostata come contesto della nuova eccezione.

..Di più qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.