Articolo originale: Error Handling in Python – try, except, else, & finally Explained with Code Examples
Di recente, il mio manager mi ha incaricato di creare un report automatico. Ho progettato il report affinché fosse semplice. Includeva un po' di numeri da un database e alcune operazioni matematiche basilari. Ero entusiasta di poter finalmente mettere in mostra in azienda le mie incredibili abilità con Python.
Ho completato e consegnato il prodotto. Tutto andava alla grande. O almeno, fino a due settimane più tardi. Il mio report ha cominciato a fallire improvvisamente a causa di un errore di divisione per zero. È il momento di far partire la risata registrata.
La mia breve storia non è particolarmente dettagliata, ma dovrebbe enfatizzare l'importanza di gestire casi limite ed errori quando si scrivono programmi. Questo report sarebbe dovuto essere un'opportunità per mettere in mostra la mia competenza in Python, invece, ne è risultata una figuraccia piuttosto imbarazzante.
Perciò, prendiamoci un momento per imparare le basi della gestione degli errori utilizzando la libreria standard di Python. Metterò in evidenza alcune delle cose di cui hai bisogno per iniziare.
Prima di iniziare a gestire eccezioni, dovresti avere una buona comprensione dei fondamenti di Python. Avrai bisogno di sapere perché le eccezioni vengono generate per poterle trattare!
Ecco cosa tratteremo:
- Istruzioni try ed except in Python
- Esecuzione condizionale con la clausola else
- Eccezioni predefinite
- Eccezioni personalizzate
- Considerazioni sulle prestazioni
Istruzioni try ed except in Python
Le istruzioni try
ed except
sono i metodi principali per trattare le eccezioni. Ecco un esempio:
x = 0
try:
print(5 / x)
except ZeroDivisionError:
print("Qualcosa è andato storto")
# Qualcosa è andato storto
Rivediamo il codice qui sopra per essere sulla stessa lunghezza d'onda:
- La riga 1 assegna il valore 0 alla variabile
x
- Le righe 2 e 3 aprono una clausola
try
e tentano di dividere 5 per la variabilex
- Le righe 4 e 5 aprono una clausola
except
per qualsiasi errore di divisione per zero (ZeroDivisionError
) e istruiscono il programma a stampare un messaggio qualora si tentasse di dividere per 0
Probabilmente avrai notato il problema: la variabile x
ha valore 0, e sto cercando di dividere 5 per x
. Neppure i migliori matematici del mondo sono in grado di dividere per 0, e nemmeno Python può farlo. Quindi, che succede?
Se non ci occupassimo dell'errore, il programma terminerebbe immediatamente, non appena cercasse di dividere 5 per x
. Dato che i programmi non sanno cosa fare con le eccezioni senza istruzioni esplicite, abbiamo creato la clausola except
nella riga 4 e fornito al programma i passi da seguire in caso di divisione per 0.
Questa è l'intera idea dietro la gestione delle eccezioni: occorre dire al programma cosa fare quando si verifica un errore che non può semplicemente ignorare. Vediamo come funzionano le clausole try
ed except
.
Analisi dell'istruzione try
Le clausole try
ed except
seguono un modello che ti permette di gestire in modo affidabile i problemi nel tuo codice. Vediamo come funziona.
Il primo passo è tentare l'esecuzione del codice nella clausola try
.
Dopodiché, abbiamo tre possibilità:
Nessun errore nella clausola try
Se il codice nella clausola try
viene eseguito senza nessun errore, il programma:
- Esegue la clausola
try
- Salta tutte le clausole
except
- Prosegue con la sua normale esecuzione
x = 1
try:
print(5 / x)
except ZeroDivisionError:
print("Qualcosa è andato storto")
print("Sto eseguendo dopo la clausola try!")
# 5.0
# Sto eseguendo dopo la clausola try!
In questo esempio modificato, puoi vedere che non ci sono problemi nella clausola try
(righe 3 e 4). Il codice verrà eseguito, la clausola except
verrà saltata e il programma riprenderà l'esecuzione dopo la terminazione delle istruzioni try
ed except
.
Errori nella clausola try e l'eccezione è specificata
Se il codice nella clausola try
genera un'eccezione e il tipo di eccezione viene specificato dopo qualsiasi parola chiave con except
, il programma:
- Salta il codice rimanente nella clausola
try
- Esegue il codice nella clausola
except
corrispondente - Prosegue con la sua normale esecuzione
x = 0
try:
print(5 / x)
except ZeroDivisionError:
print("Qualcosa è andato storto")
print("Sto eseguendo dopo la clausola try!")
# Qualcosa è andato storto
# Sto eseguendo dopo la clausola try!
Tornando al mio primo esempio, ho cambiato la nostra variabile x
di nuovo al valore 0 e ho provato a dividere 5 per x
. Ciò produce un errore di divisione per zero (ZeroDivisionError
). Dato che l'istruzione except
specifica questo tipo di eccezione, il codice in quella clausola viene eseguito prima che il programma riprenda la sua normale esecuzione.
Errori nella clausola Try e l'Eccezione NON è Specificata
Infine, se il programma genera un'eccezione nella clausola try
, ma l'eccezione non viene specificata in nessuna istruzione except
, allora il programma:
- Interrompe l'esecuzione del programma e dà errore
x = 0
try:
print(5 / y)
except:
print("Qualcosa è andato storto")
print("Sto eseguendo dopo la clausola try!")
# NameError: name 'y' is not defined (il nome 'y' non è definito)
Nell'esempio precedente, sto cercando di dividere 5 per la variabile y
, che non esiste. Questo genera un errore di nome (NameError
). Non abbiamo specificato al programma come gestire i NameError
, perciò l'unica opzione è quella di terminare.
Pulizia
try
ed except
sono gli strumenti principali della gestione degli errori, ma esiste anche una clausola opzionale che puoi usare, chiamata finally
. La clausola finally
verrà sempre eseguita, che ci sia un errore o meno.
x = 0
try:
print(5 / x)
except ZeroDivisionError:
print("Sono la clausola except!")
finally:
print("Sono la clausola finally!")
print("Sto eseguendo dopo la clausola try!")
# Sono la clausola except!
# Sono la clausola finally!
# Sto eseguendo dopo la clausola try!
In questo esempio, ho creato il nostro amato ZeroDivisionError
. Puoi vedere che l'ordine di esecuzione è:
- La clausola
except
- La clausola
finally
- Qualsiasi codice successivo
Una volta che correggiamo il codice nella clausola try
in modo che non generi più errori, vedrai comunque un ordine di esecuzione simile. Al posto della clausola except
, verrà eseguita la clausola try
.
x = 1
try:
print(5 / x)
except ZeroDivisionError:
print("Sono la clausola except!")
finally:
print("Sono la clausola finally!")
print("Sto eseguendo dopo la clausola try!")
# 5.0
# Sono la clausola finally!
# Sto eseguendo dopo la clausola try!
Noterai che l'unica differenza è che la clausola try
viene eseguita con successo perché non vengono generate eccezioni. La clausola finally
e il codice successivo vengono eseguiti come previsto.
Questo è utile in alcuni casi in cui vuoi dare una "ripulita" indipendentemente dall'esito delle clausole try
e except
. Azioni come chiudere connessioni, chiudere file e fare logging sono ottimi candidati per la clausola finally
.
Esecuzione condizionale con la clausola else
Un'altra clausola opzionale è la clausola else
. La clausola else
è semplice: se il codice nella clausola try
viene eseguito senza generare errori, allora anche il codice nella clausola else
verrà eseguito.
x = 1
try:
print(5 / x)
except ZeroDivisionError:
print("Sono la clausola except!")
else:
print("Sono la clausola else!")
finally:
print("Sono la clausola finally!")
print("Sto eseguendo dopo la clausola try!")
# 5.0
# Sono la clausola else!
# Sono la clausola finally!
# Sto eseguendo dopo la clausola try!
L'ordine di esecuzione per questo esempio è:
- La clausola
try
- La clausola
else
- La clausola
finally
- Qualsiasi codice dopo
Se dovessimo incontrare un'eccezione o un errore nella clausola try
, la clausola else
verrebbe ignorata.
x = 0
try:
print(5 / x)
except ZeroDivisionError:
print("Sono la clausola except!")
else:
print("Sono la clausola else!")
finally:
print("Sono la clausola finally!")
print("Sto eseguendo dopo la clausola try!")
# Sono la clausola except!
# Sono la clausola finally!
# Sto eseguendo dopo la clausola try!
Eccezioni predefinite
Mi hai visto scrivere a proposito di due diversi errori finora: NameError
e ZeroDivisionError
. Ma se avessimo bisogno di altre eccezioni?
Esiste un'intera lista di eccezioni predefinite in Python che fanno parte della libreria standard. Probabilmente coprono quasi tutte le necessità che potresti avere nella gestione di errori ed eccezioni.
Ecco solo alcune che potrebbero essere importanti:
KeyError
– Una chiave non viene trovata in un dizionarioIndexError
– L'indice è fuori dai limiti stabiliti di un oggetto iterabileTypeError
– Una funzione o un'operazione è stata usata su un tipo di oggetto sbagliatoOSError
– Errori generali del sistema operativo
Ce ne sono molte altre, che puoi trovare nella documentazione di Python. Consiglio di darci un'occhiata. Non solo diventerai più bravo a gestire gli errori, ma esplorerai anche cosa può effettivamente andare storto nei i tuoi programmi Python.
Eccezioni personalizzate
Se hai bisogno di funzionalità estese, puoi anche definire delle eccezioni personalizzate.
class ForError(Exception):
def __init__(self, message):
self.message = message
def foo(self):
print("bar")
Nell'esempio sopra, ho creato una nuova classe e l'ho estesa dalla classe Exception. Ora posso scrivere una funzionalità personalizzata e trattare questa eccezione come qualsiasi altro oggetto.
try:
raise FooError("Questo è un errore di test")
except FooError as e:
e.foo()
# bar
Qui ho generato di proposito il mio nuovo FooError
. Ho catturato FooError
e gli assegno l'alias e
. Ora posso accedere al mio metodo foo()
che ho costruito nella classe che ho creato.
Questo apre un mondo di possibilità quando si ha a che fare con gli errori. Log personalizzati, tracciamenti più dettagliati, o qualsiasi altra cosa di cui hai bisogno, tutto può essere codificato e creato.
Considerazioni sulle prestazioni
Ora che conosci le basi di try
, except
e degli oggetti eccezione, puoi cominciare a considerare di usarli nel tuo codice per gestire gli errori in modo elegante. Ma ci sono impatti significativi sulle prestazioni del codice?
La risposta breve è no. Con il rilascio di Python 3.11, non c'è praticamente nessuna riduzione di velocità nell'uso delle istruzioni try
ed except
quando non vengono sollevate eccezioni.
La cattura degli errori ha causato effettivamente qualche rallentamento. Ma in generale, è meglio catturare questi errori piuttosto che far crashare l'intero programma.
Nelle versioni precedenti di Python, usare le clausole try
ed except
causava del tempo di esecuzione aggiuntivo. Tieni presente questo dettaglio se non hai la versione aggiornata.
Ricapitolando
Grazie per aver letto fino a qui. Il futuro te e i tuoi futuri clienti ti ringrazieranno per la gestione degli errori.
Abbiamo esaminato le clausole try
, except
, else
, e finally
, il loro ordine di esecuzione e in quali circostanze vengono eseguite. Abbiamo anche visto le basi per creare eccezioni personalizzate.
La cosa più importante da ricordare è che le clausole try
ed except
sono i modi principali per gestire gli errori e dovresti usarle ogni volta che hai del codice rischioso e che potrebbe generare degli errori.
Inoltre, tieni a mente che la cattura degli errori renderà il tuo codice più resiliente e ti farà sembrare un programmatore decisamente migliore.