Nel precedente articolo abbiamo introdotto la sintassi, ora è giunto il momento di parlare delle eccezioni in Python.
Negli esempi presentati finora abbiamo già avuto a che fare, anche se in modo superficiale, con le eccezioni (exception) di Python.
Il meccanismo delle eccezioni è presente anche in altri linguaggi di programmazione come Java e C++ ed è ampiamente collaudato.
Le eccezioni sono eventi scatenati da errori di varia natura.
- Quando prevediamo che in un dato contesto si possa verificare un’eccezione, possiamo scrivere del codice per “gestirla” (in inglese to handle).
Si parla dunque di handled exception.
- Se invece un’eccezione si verifica in un contesto imprevisto, non viene “gestita” (unhandled exception, eccezione non gestita) e quindi causerà l’uscita dal blocco di codice in cui si presenta.
Se però il blocco di codice più esterno ha “previsto” l’eccezione scatenata, potrà occuparsene.
In caso contrario, l’esecuzione uscirà anche da questo blocco e così via: alla fine potrebbe provocare l’uscita dal blocco più esterno, ovvero dal programma stesso.
Vediamo ora cosa vuol dire gestire e anche “sollevare” (da to raise in inglese) un’eccezione.
Potrebbe sembrare assurdo il fatto che sia possibile “sollevare” un’eccezione, visto che il nostro compito dovrebbe essere quello di prevederle.
Uno degli aspetti più interessanti delle eccezioni è proprio questo: la possibilità di sollevare un’eccezione in modo che altre parti del programma la gestiscano.
Gestire le eccezioni in Python: istruzioni try ed except
Per imparare a gestire le eccezioni in Python, vediamo come generarne una.
Una tra le eccezioni più semplici da provocare è, senza dubbio, quella generata dalla divisione per zero:
In questo caso, l’eccezione è stata generata in modalità interattiva con IDLE.
Per questo motivo ci siamo trovati di nuovo con il prompt di Python in attesa di ricevere nuovi comandi, come se niente fosse.
Vediamo cosa accade invece se generiamo la stessa eccezione in uno script come il seguente, che chiameremo div_by_zero.py:
L’output di questo script:
Il nostro programma è entrato in crash (in inglese crash significa crollare). Non c’è traccia del nostro messaggio finale.
Proviamo ora a gestire l’eccezione con questa nuova versione dello script div_by_zero2.py:
L’output dello script è diverso:
Come possiamo notare: invece delle righe incomprensibili (almeno per ora) dell’output precedente, vengono presentate informazioni più chiare e abbiamo anche il messaggio finale del nostro programma.
- Osservando la nostra versione dello script può sorgere spontaneamente qualche domanda:
– Cosa sarebbe successo se ci fossero state altre istruzioni tra l’errore e l’except ?
– Se invece non ci fosse stata alcuna eccezione cosa sarebbe accaduto alle istruzioni contenute nell’except ?
– E se poi fosse stata sollevata un’eccezione diversa da ZeroDivisionError ?
E’ pertanto importante comprendere il funzionamento dell’istruzione composta try…except.
Come funziona l’istruzione try…except?
- Se non viene sollevata alcuna eccezione, il blocco di istruzioni che segue il try viene eseguito fino in fondo. Quindi il controllo passa alle istruzioni che seguono il blocco except, il quale non viene quindi eseguito.
- Al contrario, se all’interno del blocco try viene sollevata un’eccezione, allora tutte le eventuali istruzioni che seguono quella che ha scatenato l’errore, vengono saltate e il controllo passa immediatamente al blocco except, sempre che questo contenga l’eccezione corretta.
- Se invece l’except riguarda un altro tipo di eccezione, viene saltata l’esecuzione dell’intero blocco di istruzioni che contiene il try e il controllo passa al blocco più esterno, che forse offre un except di tipo corretto e in ultima istanza al sistema operativo.
- E’ ovviamente possibile prevedere più blocchi except per ogni try, ognuno con le istruzioni dedicate a uno o più tipi di eccezioni. L’ultima clausola except può non specificare alcuna eccezione; in questo caso quest’ultima clausola (denominata default except ) individuerà ogni eccezione non gestita precedentemente.
Vediamo una porzione di codice contenente due except:
Come vediamo, sono previsti due blocchi except per due diversi tipi di eccezioni: TypeError e NameError.
- Dagli esempi appena mostrati, notiamo come nel primo caso, dove non viene dichiarato arg, venga eseguito il blocco del secondo except.
- Nel secondo caso invece, arg viene dichiarato, ma il suo tipo non è accettabile peri l comando dict; pertanto viene eseguito il blocco del primo except.
Sollevare un’eccezione in Python: il comando raise
Il comando raise permette di “sollevare” un’eccezione in Python, oppure, se viene chiamato senza parametri, di passare al controllo di un’eccezione al blocco di istruzioni più esterno.
Ecco un esempio che presenta entrambe le situazioni:
La prima riga di output è quella che abbiamo gestito direttamente nel corpo del try e la seconda è prodotta dal blocco except. In entrambi i casi viene impiegata l’istruzione print.
Quindi, dopo aver eseguito un raise senza parametri, il controllo è stato restituito all’interprete interattivo, il quale ha visualizzato le ultime righe che contengono il tipo di eccezione oltre al messaggio “Bingo!” che abbiamo chiesto di visualizzare.
Le istruzioni finally ed else
Il blocco try…except per le eccezioni in Python, prevede la clausola opzionale finally e, come per il for, anche else.
- Il blocco di istruzioni contenuto in finally viene eseguito sempre e comunque (finally in inglese significa “alla fine” e non “finalmente”).
- Quello contenuto in else viene invece eseguito solo se tutto è andato bene e non sono state sollevate eccezioni.
Il seguente script di esempio finally-else.py ci chiarirà le idee meglio di qualsiasi definizione:
Le prime tre righe del programma possono non essere del tutto chiare:
la prima importa la libreria sys
la seconda e la terza caricano nelle due variabili nominatore e denominatore i primi due parametri passati a finally-else.py dalla riga di comando.
Adesso osserviamo due esecuzioni distinte dello script:
- nella prima definiremo volutamente un divisore uguale a zero
- nella seconda forniremo due valori corretti
Nell’output dello script notiamo che:
- nel primo caso, dove viene sollevata un’eccezione, viene visualizzato il messaggio d’errore (except) e infine il messaggio di conclusione del programma (finally).
- nel secondo caso, senza eccezioni, viene visualizzato il messaggio contenente il risultato (else) e infine, nuovamente, il messaggio di conclusione del programma (finally).
La funzione exc_info
Nell’ultimo esempio abbiamo visto come importare la libreria sys e come usare la lista argv che corrisponde all’elenco dei parametri passati dalla riga di comando.
Nella libreria sys esiste anche una funzione utilissima che ha a che fare con le eccezioni in Python: exc_info.
Quando ci troviamo a gestire un’eccezione sconosciuta, per esempio con una default except, come possiamo accedere alla descrizione dell’ultima eccezione?
Proprio con exc_info.
Un blocco except senza nessun tipo di eccezione indicata è una pratica sconsigliabile:
si corre il rischio di nascondere problemi totalmente imprevisti in fase di programmazione.
E’ sempre meglio specificare le eccezioni in Python per le quali è stato definito un blocco try.
Vediamo come usare questa funzione con lo script finally-else2.py:
Di seguito, l’output dello script:
I tre valori della lista sys.exc_info sono rispettivamente:
- il tipo di eccezione scatenata
- l’eventuale valore a essa associato (ovvero l’eventuale parametro con cui è inizializzata l’eccezione sollevata da raise )
- un oggetto traceback.
Il traceback è un oggetto avanzato che permette di visualizzare lo stack del programma al momento in cui si è verificata l’eccezione.
In parole povere, l’elenco di script attualmente caricati da Python, a partire da quello più “esterno”.
Nell’esempio precedente, se avessimo visualizzato il traceback con la funzione print_tb dell’omonimo modulo, avremmo ottenuto solo la seguente riga:
File “finally-else2.py”, line 5, in <module>.
Abbiamo concluso così anche le eccezioni in Python, non ci resta che passare a funzioni, classi e input/output.
Se hai saltato qualche argomento, niente paura: trovi tutto nella sezione dedicata alla guida alla programmazione con Python!
Iscriviti alla nostra newsletter per rimanere aggiornato sui prossimi articoli!
5 risposte