2.25. Gestione degli errori

La maggior parte dei problemi a runtime in Python si manifesta come eccezioni – un modo strutturato e con un nome per segnalare che qualcosa è andato storto. ValueError, TypeError, KeyError, OSError, MemoryError sono tutti esempi; ciascuna è una classe, e sollevare (raising) una di esse interrompe la chiamata corrente e cerca un gestore nel codice circostante.

2.25.1. try / except

Racchiudi un blocco di codice in try per intercettare qualsiasi eccezione esso sollevi:

try:
    n = int(input_text)
except ValueError:
    n = 0

Se la conversione int(...) fallisce, il controllo passa al blocco except invece di propagare ulteriormente l’errore. Se input_text era una stringa intera valida, il blocco except viene saltato.

Un singolo try può avere diversi blocchi except, ciascuno dei quali intercetta un diverso tipo di errore:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python confronta nell’ordine; il primo la cui classe di eccezione corrisponde gestisce il problema. Intercettare Exception (la classe base per quasi tutto) gestisce qualsiasi errore; riserva questa pratica al livello più esterno di un programma, dove l’alternativa è un crash.

Avvertimento

Un except: nudo (senza classe dopo la parola chiave) intercetta anche KeyboardInterrupt – l’eccezione che l’IDE invia quando premi il suo pulsante stop per interrompere uno script in esecuzione. Un ciclo racchiuso in un except: pass nudo inghiottirà l’interruzione e continuerà a girare, senza lasciare alcun modo di fermare lo script se non spegnendo e riaccendendo.

Preferisci except Exception: rispetto a except: nudo quando hai davvero bisogno di intercettare in modo ampio. KeyboardInterrupt eredita da BaseException, non da Exception, quindi except Exception: lascia funzionante il pulsante stop.

2.25.1.1. Esaminare l’eccezione

Per leggere il messaggio associato a un’eccezione, assegnale un nome con as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

La variabile associata da as è valida solo all’interno del blocco except.

2.25.2. else e finally

Un blocco try ha due elementi opzionali aggiuntivi.

else viene eseguito solo quando il try è terminato senza sollevare eccezioni:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

Mettere «cosa fare quando ha funzionato» in else mantiene stretto il blocco try – solo la riga che potrebbe fallire appartiene a try.

finally viene eseguito alla fine in ogni caso – che il try abbia avuto successo, abbia sollevato un’eccezione che è stata gestita, oppure abbia sollevato un’eccezione che sta per propagarsi:

try:
    do_work()
finally:
    cleanup()
Diagramma di flusso che mostra i quattro possibili percorsi attraverso try / except / else / finally: il successo va a else e poi a finally; un'eccezione intercettata va a except e poi a finally; un'eccezione non intercettata va a finally e poi si propaga.

finally viene sempre eseguito. else viene eseguito solo sul percorso senza eccezioni.

Per la maggior parte degli schemi di acquisizione/rilascio, preferisci un context manager rispetto a una coppia try / finally – la risorsa stessa gestisce la propria pulizia.

2.25.3. Eccezioni built-in comuni

Un breve elenco delle eccezioni in cui ti imbatterai spesso:

  • ValueError – tipo corretto, valore errato (bytes([300]) – 300 è del tipo corretto, ma è al di fuori dell’intervallo di byte valido 0..255).

  • TypeError – tipo del tutto sbagliato (len(42)).

  • KeyError – chiave mancante in un dict.

  • IndexError – indice oltre la fine di una sequenza.

  • AttributeError – accesso a un attributo che non esiste ("abc".foo).

  • OSError – un errore del filesystem o di I/O.

  • MemoryError – heap esaurito. Su un runtime con memoria limitata, questo può accadere durante il normale funzionamento – non solo in casi patologici.