back to top

Gestione delle eccezioni in Python

Possiamo definire un’eccezione come il costrutto di un linguaggio che, in presenza di un’anomalia, genera un messaggio di errore. Quando si verifica un problema durante il funzionamento di un processo, quest’ultimo viene arrestato e, nel caso in cui l’anomalia non venga gestita l’applicazione smetterà di funzionare dpo la sua segnalazione.

Eccezioni predefinite

Python presenta decine di eccezione predefinite e ognuna di esse è dedicata a segnalare anomalie specifiche. Dal punto di vista di chi si avvicina per la prima volta alla programmazione con questo linguaggio l’eccezione più interessante è sicuramente SyntaxError, essa infatti ha il compito di segnalare eventuali errori avvenuti durante la digitazione del codice.

Un semplice esempio di errore sintattico è quello relativo all’impiego di un formato imprevisto per la definizione di un costrutto:

# Notifica di errore sintattico in un costrutto condizionale
>>> x = 10
>>> if x > 4
SyntaxError: invalid syntax

Come è possibile osservare, la digitazione del codice precedente produce la notifica di errore "SyntaxError: invalid syntax". Quest’ultima è composta dal riferimento all’eccezione che ha inviato la segnalazione ("SyntaxError") e dalla descrizione del tipo di anomalia verificatasi ("invalid syntax").

Ciò accade perché l’interprete di Python è in grado rilevare la presenza di sintassi malformata, nel caso specifico la mancanza dei due punti alla fine della condizione, e risponde a questo evento richiamando l’eccezione predefinita più adatta per segnalare l’anomalia individuata.

Errori a runtime

Nonostante il termine "eccezione" venga utilizzato in modo abbastanza generico per identificare qualsiasi evento anomalo possa impedire il corretto funzionamento di un processo, sarebbe meglio distinguerlo dall’altrettanto generico concetto di "errore". Le anomalie sintattiche sono da considerarsi in tutto e per tutto degli errori (da parte dello sviluppatore), esse infatti precedono la fase di esecuzione e vengono prodotte durante la stesura del codice.

Diverso è il caso dei cosiddetti errori a runtime, cioè quelli che si verificano in esecuzione e per i quali "eccezione" risulta essere un termine più appropriato.

Un esempio chiarificatore potrebbe essere quello dell’apertura di un file non disponibile:

# Apertura di un file esistente
>>> open('C:\\Python\\README.txt','rt)
<_io.TextIOWrapper name='C:\\Python\\README.txt' mode='r' encoding='cp1252'>
>>> open('C:\\Python\\README.txt','rt')
<_io.TextIOWrapper name='C:\\Python\\README.txt' mode='rt' encoding='cp1252'<

Apertura di un file inesistente e output di errore
>>> open('C:\\Python\\README2.txt','rt')
Traceback (most recent call last):
  File "<pyshell#33>", line 1, in <module>
    open('C:\\Python\\README2.txt','rt')
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Python\\README2.txt'

In questo caso infatti la sintassi utilizzata è corretta e il malfunzionamento è dovuto invece all’assenza della risorsa desiderata. L’interprete utilizza quindi l’eccezione di default FileNotFoundError, e non "SyntaxError", perché più adatta alla gestione di questa casistica.

Verrebbe visualizzata una segnalazione di errore simile anche se si cercasse di accedere al contenuto di una directory inesistente:

# Tentativo di accesso ad una directory inesistente
>>> import os
>>> os.listdir('C:\\Python\\Lib\\directory_inesistente')
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    os.listdir('C:\\Python\\Lib\\directory_inesistente')
FileNotFoundError: [WinError 3] Impossibile trovare il percorso specificato: 
'C:\\Python\\Lib\\directory_inesistente'

Così come verrebbe generato un errore a runtime nel caso in cui si provasse ad importare un modulo non disponibile:

# Richiamare un modulo inesistente
>>> import nomodule
Traceback (most recent call last):
  File "<pyshell#36>", line 1, in <module>
    import nomodule
ImportError: No module named 'nomodule'

Un’istruzione del genere richiamerebbe infatti l’eccezione predefinita ImportError, appositamente concepita per questa tipologia di evento.

Exception Handling: try, raise ed except

L’Exception Handling è una pratica che ci consente di gestire le eccezioni in Python, essa si basa sulla delimitazione del codice in blocchi all’interno dei quali lanciare le istruzioni e determinare dei comportamenti conseguenti all’eventuale verificarsi di errori.

Un semplice esempio di Exception Handling potrebbe essere quello presentato nel seguente listato:

# Exception Handling in Python
>>> try:
x = int(input("Digita un intero uguale o inferiore a 9: "))
if x > 9:
raise ValueError("Numero superiore a 9")
except ValueError as y:
print(y)

L’applicazione presentata richiede un input all’utente: la digitazione di un numero intero che sia inferiore o uguale al valore associato alla variabile "x", nel caso specifico "9". Digitando un valore superiore a quello della variabile si riceverà in output la notifica di errore "Numero superiore a 9". Come è possibile ottenere questo risultato?

Si osservi come il codice venga introdotto dalla keyword try che ha il compito di inizializzare il blocco di controllo sulle eccezioni. Il messaggio di errore previsto viene inviato in output nel caso in cui venga intercettata l’eccezione ValueError che l’interprete richiama quando un valore è sì di tipo corretto, ma di entità diversa da quella attesa (superiore a 9 nell’esempio).

La keyword raise consente quindi di definire la modalità di gestione dell’eccezione, mentre la clausola except permette all’interprete di identificare l’eccezione da gestire. Tale clausola diventa particolarmente utile nel caso di eccezioni multiple, perché un singolo blocco try potrebbe doverne gestire più di una.

Exception Handling: try e finally

Un blocco try può prevedere la clausola finally con la quale definire un’istruzione che verrà eseguita comunque, che si verifichi o meno l’errore che si intende gestire. Si analizzi il codice seguente:

# Utilizzo della clausola finally
>>> try:
   f = open("nome_file.txt",encoding = 'utf-8')
   # eventuali operazioni sul file
   #...
finally:
   f.close()

In questo caso viene richiesta l’apertura di un file, le successive operazioni su di esso potrebbero dar luogo ad eccezioni e bloccare l’applicazione, ma l’istruzione introdotta dalla clausola consentirà di chiudere comunque la risorsa aperta, indipendentemente da qualsiasi problematica possa verificarsi.

Pubblicità
Claudio Garau
Claudio Garau
Web developer, programmatore, Database Administrator, Linux Admin, docente e copywriter specializzato in contenuti sulle tecnologie orientate a Web, mobile, Cybersecurity e Digital Marketing per sviluppatori, PA e imprese.