17. Gestione degli Errori

In questa lezione impareremo a gestire eventuali errori che si possano manifestare nei nostri programmi, e per fare ciò parleremo delle istruzioni try, except e finally.


Eccezioni di Python e Messaggi di Errore

Parlando di errori mi riferisco in realtà a quelle eccezioni (in inglese exception) di Python che quando si manifestano, comportano spesso il crash del programma e la comparsa di un messaggio di errore. Otteniamo ad esempio un'eccezione del tipo NameError, provando a moltiplicare una variabile che non è stata definita.

>>> z * 5
NameError: name 'z' is not defined

Un altra tipologia di errore ci viene mostrata se proviamo ad effettuare una divisione per zero, che restituisce ZeroDivisionError:

>>> 3/0
ZeroDivisionError: division by zero

Questi messaggi di errore sono molto comodi per noi sviluppatori, in quanto fornendoci dettagli relativi a un comportamento anomalo del nostro programma, ci permettono di risolvere i vari bug che inevitabilmente si presentano.

Ma non tutti gli errori sono bug.

Ad esempio è più che normale ottenere ZeroDivisionError provando a fare 3 / 0.

In casi come questi è quindi importante gestire queste eccezioni, per rendere i nostri programmi più robusti e user friendly.


Le istruzioni try ed except in Python

Le istruzioni principali che vengono usate per questo scopo sono try ed except, che possono essere tradotte rispettivamente come prova e eccetto o ad eccezione di.

Volendo potete pensare a queste come a degli if / else ideati per la gestione delle eccezioni, in quanto anche in questo caso definiremo dei blocchi di codice distinti.

Il codice del blocco try verrà eseguito qualora tutto andasse liscio e senza errori, mentre il codice inserito nel blocco except verrà eseguito solamente qualora si dovesse manifestare l'eccezione ad esso associato.

Abbiamo qui di seguito una funzione moltiplicatore che richiede all'utente di inserire dei valori per le variabili a e b, ed effettua quindi una moltiplicazione.

Proviamo ad eseguire il codice e a forzarne il comportamento ipotizzato introducendo dei caratteri non numerici all'interno delle variabili a e b:

def moltiplicatore():
    a = int(input('Inserisci il valore di a: '))
    b = int(input('Inserisci il valore di b: '))
    risultato = a * b
    print(risultato)


moltiplicatore()

# output
Inserisci il valore di a: eggs
ValueError: invalid literal for int() with base 10: 'eggs'

Abbiamo ottenuto ValueError, ovvero errore di valore. Questo ci viene restituito perché la funzione int() non è in grado di convertire la stringa eggs in un numero intero.

Vediamo ora di usare le istruzioni try ed except per gestire questa eventualità, ed eseguiamo nuovamente il codice.

def moltiplicatore():
    try:
        a = int(input("Inserisci il valore di a: "))
        b = int(input("Inserisci il valore di b: "))
        risultato = a * b
        print(risultato)
    except ValueError:
        print("'Hey tu! solo caratteri numerici, grazie!")

moltiplicatore()

# output
Inserisci il valore di a: bacon
Hey amico! solo caratteri numerici, grazie!

Ed ecco che ora va decisamente meglio! Il nostro programma gestisce l'eccezione senza crashare, mostrando invece un messaggio informativo ai nostri utenti.

Quindi qualora siate in fase di progettazione di un programma, e notate che ci sono delle eventualità in cui, per quanto il programma sia ben strutturato, si arriva comunque a dei crash, come appunto in questi casi presentati, semplicemente copiate il nome dell'errore e usatelo assieme ad except per poter gestire questa eccezione. Così facendo il programma avrà tutto un altro livello di professionalità!


L'istruzione finally

Un'altra istruzione di Python utile in questi contesti è l'istruzione finally, traducibile in italiano come alla fine o infine. Come il nome stesso suggerisce, il codice definito nel blocco di finally verrà eseguito alla fine del programma qualsiasi cosa succeda, sia che si manifesti un errore oppure no.

Modifichiamo la nostra funzione usando questa istruzione:

def moltiplicatore():
    try:
        a = int(input("Inserisci il valore di a: "))
        b = int(input("Inserisci il valore di b: "))
        risultato = a * b
        print(risultato)
    except ValueError:
        print("Hey tu! solo caratteri numerici, grazie!")
    finally:
        print("Sto chiudendo l'applicazione!")

moltiplicatore()

# eseguiamo il codice cercando di innescare l'errore
Inserisci il valore di a: poker
Hey tu! solo caratteri numerici, grazie!
Sto chiudendo l'applicazione!

# eseguiamo nuovamente il codice senza innescare l'errore
Inserisci il valore di a: 2
Inserisci il valore di b: 3
6
Sto chiudendo l'applicazione!

Come notate ora la stringa "Sto chiudendo l'applicazione!" viene mandata in print qualsiasi cosa succeda!

Nota: evitate di usare le istruzioni try ed except su tutto il codice di una funzione, ma cercate sempre di racchiuderci solamente il codice necessario


Come mostrare i messaggi di errore

Un'altra cosa che possiamo fare per migliorare il codice è mostrare in output il messaggio di errore.

Nel blocco dell'except, dopo il nome dell'eccezione che stiamo gestendo, possiamo aggiungere as e, e quindi mandare in output il valore di e tramite print():

def moltiplicatore():
    try:
        a = int(input("Inserisci il valore di a: "))
        b = int(input("Inserisci il valore di b: "))
        risultato = a * b
        print(risultato)
    except ValueError as e:
        print(f"Errore: {e}")
        print("Hey tu! solo caratteri numerici, grazie!")
    finally:
        print("Sto chiudendo l'applicazione!")

moltiplicatore()


# output
Inserisci il valore di a: spam
Errore: invalid literal for int() with base 10: 'spam'
Hey tu! solo caratteri numerici, grazie!
Sto chiudendo l'applicazione!