1
Gestire gli errori e richiedere aiuto
Per favore, non antropomorfizzate i computer, perché lo trovo molto noioso. Quando i computer vi mostrano un messaggio di errore, non è perché li avete offesi. I computer sono forse gli strumenti più sofisticati con cui la maggior parte di noi interagirà, tuttavia restano comunque degli strumenti.
Nonostante ciò, è facile dare la colpa ai computer. Dato che buona parte dell’apprendimento della programmazione è auto-referenziale, è normale avvertire il fallimento quando dovete continuamente consultare Internet svariate volte al giorno, anche se state studiando Python da mesi. Tuttavia anche gli sviluppatori professionisti consultano Internet o la documentazione per rispondere alle loro domande di programmazione.
A meno che non abbiate le risorse finanziarie o sociali per assumere un tutor privato che possa rispondere alle vostre domande, sarete limitati al vostro computer, ai motori di ricerca su Internet e alla vostra forza d’animo. Fortunatamente le vostre domande sono quasi certamente già state poste in precedenza. Come programmatori, essere in grado di trovare le risposte per conto vostro è molto più importante che conoscere qualunque algoritmo o struttura di dati. Questo capitolo vi guiderà attraverso la conoscenza di questa skill fondamentale.
Come capire i messaggi di errore di Python
Quando si trovano di fronte a un vero e proprio muro di messaggi testuali tecnologici, il primo impulso di molti programmatori è di ignorarli completamente. Tuttavia in questi messaggi di errore si trova la risposta su ciò che non funziona nel vostro programma. Trovare questa risposta è un procedimento diviso in due passi: esaminare il traceback ed eseguire una ricerca su Internet con il messaggio di errore.
Esaminare i traceback
I programmi in Python vanno in crash quando il codice solleva un’eccezione che la dichiarazione except non riesce a gestire. Quando ciò accade, Python visualizza il messaggio dell’eccezione e un traceback. Chiamato anche stack trace, il traceback mostra il punto all’interno del programma in cui è avvenuta l’eccezione e il percorso di chiamate alle funzioni che l’ha attivata.
Per fare pratica nella lettura dei traceback, scrivete il seguente programma, che contiene dei bug, e salvatelo come abcTraceback.py. I numeri di riga sono solo un riferimento e non fanno parte del programma.
In questo programma la funzione a() chiama b() ❶, che a sua volta chiama c() ❷. All’interno di c(), l’espressione 42 / 0 ❸ provoca un errore di divisione per zero. Quando eseguite questo programma, l’output dovrebbe assomigliare al seguente:
Esaminiamo questo traceback riga per riga, iniziando da questo punto:
Questo messaggio vi fa sapere che quello che segue è un traceback. Il testo most recent call last indica che ciascuna delle chiamate alle funzioni è elencata in ordine, a partire dalla prima chiamata per terminare con quella più recente.
La riga successiva mostra la prima chiamata a una funzione del traceback:
Queste due righe rappresentano il frame summary e mostrano le informazioni all’interno di un oggetto frame. Quando una funzione viene invocata, i dati della variabile locale, insieme al punto all’interno del codice in cui deve restituire informazioni, sono memorizzati in un oggetto frame. Gli oggetti frame contengono le variabili locali e altri dati associati alle chiamate alle funzioni. Gli oggetti frame vengono creati quando la funzione viene chiamata e distrutti quando la funzione esegue il return. Il traceback mostra un frame summary per ciascun frame che porta al crash. Possiamo vedere che la chiamata alla funzione è sulla riga 13 di abcTraceback.py, mentre il testo <module> ci informa che questa riga è nel global scope. La riga 13 viene mostrata con due spazi di indentazione.
Le quattro righe che seguono rappresentano i due prossimi frame summary:
Grazie al testo line 3, in a possiamo capire che b() è stata chiamata nella riga 3 all’interno della funzione a(), cosa che ha fatto sì che c() venisse chiamata nella riga 7 all’interno della funzione b(). Notate che le chiamate print() alle righe 2, 6 e 10 non vengono mostrate nel traceback, anche se sono state eseguite prima che avvenissero le chiamate alle funzioni. Nel traceback vengono visualizzate solo le righe che contengono le chiamate a funzioni che hanno portato all’eccezione.
L’ultimo frame summary mostra la riga che ha provocato l’eccezione di tipo unhandled, seguita dal nome dell’eccezione e dal relativo messaggio:
Notate che il numero di riga prodotto dal traceback è il punto in cui Python ha rilevato un errore. La vera fonte del bug potrebbe trovarsi da qualche parte prima di questa riga.
I messaggi di errore sono notoriamente brevi e imperscrutabili: le tre parole division by zero non vi comunicano nulla in particolare, a meno che non sappiate che la divisione per zero è matematicamente impossibile e che si tratta di un bug frequente dei software. In questo programma il bug non è difficile da scovare. Osservando la riga di codice nel frame summary, è piuttosto chiaro che nel testo 42 / 0 si sta verificando una divisione per zero.
Diamo però un’occhiata a un caso più complesso. Scrivete questo codice in un editor di testo e salvatelo come zeroDivideTraceback.py:
Quando eseguite questo programma, l’output dovrebbe essere simile a questo:
Il messaggio di errore è il medesimo, ma...