
Sviluppare software può essere un compito difficile, specialmente per progetti “grandi”. Le aspettative sono quelle per cui premiamo un pulsante e tutto funziona bene e senza intoppi. Peccato che la realtà sia un po’ più complicata di così… Ciò che vogliamo dire è che i sistemi creati con il software sono fragili, e che gli errori sono nascosti in ogni riga di codice e possono danneggiare il software, portando all’interruzione del programma. In questo articolo, descriveremo alcuni intoppi tipici che possiamo incontrare e come procedere a una efficace gestione degli errori in Python tramite le eccezioni.
Perché la gestione degli errori è così importante?
La gestione degli errori in Python non è altro che la capacità di comprendere che ogni pezzo di codice possa fallire per diverse ragioni.
Questa capacità – che si sviluppa con l’esperienza pratica sviluppando codice – consente ai programmatori di anticipare gli errori possibili che possono verificarsi in un pezzo di codice o in un programma.
Questa anticipazione fa sì che i programmatori esperti preparino ”il terreno per una controffensiva” in modo tale che il codice non si interrompa a causa di un errore.
In altre parole, l’obiettivo di un programmatore non è solo creare software. È anche creare software che non si interromperà quando gli utenti andranno ad utilizzarlo.
Per essere sicuri che il codice non si interrompa, abbiamo due opzioni in Python:
- Gestione delle eccezioni.
- Unit tests.
Questo articolo è un’introduzione alla gestione delle eccezioni in Python.
Si consideri che un programma ben scritto generalmente ha bisogno sia della gestione delle eccezioni che degli unit tests. Quindi, gestire solamente le eccezioni non è sufficiente per essere sicuri che il codice non si interrompa.
Ma, a tutti gli effetti, è un buon punto di partenza.
Lettura consigliata: Come sviluppare il tuo Chatbot con Python e ChatterBot partendo da zero
Gestione degli errori in Python: i più comuni
Vediamo alcuni intoppi comuni che possono verificarsi durante la scrittura del codice e come procedere alla gestione degli errori in Python in modo da poter capire come anticiparli quando scriviamo il codice.
SyntaxError
In Python, si verifica un SyntaxError quando l’interprete incontra codice che viola le regole di sintassi del linguaggio. Queste regole determinano come dovrebbe essere strutturato il codice Python, inclusa la corretta indentazione, l’uso corretto delle parole chiave e il posizionamento di parentesi, parentesi graffe e dei due punti.
Quando l’interprete incontra un SyntaxError, non può procedere con l’esecuzione del programma finché l’errore non viene corretto.
Vediamo un paio di esempi di codice.
Esempio 1: mancanza di parentesi
print("Hello, World"
>>>
Input In [1]
print("Hello, World"
^
SyntaxError: unexpected EOF while parsing
Code language: PHP (php)
Qui l’interprete Python ci dice che c’è un SyntaxError. In particolare, “unexpected EOF while parsing” in Python sta per “fine del file inaspettata durante l’analisi”. Questo errore si verifica quando l’interprete Python incontra una situazione in cui si aspetta più codice (come la fine di un’istruzione o di un blocco), ma il file o l’input che sta leggendo termina prematuramente.
Possiamo vedere che Python ci aiuta a capire dove sta l’errore con il simbolo ^. Infatti manca una parentesi per chiudere il metodo print().
Esempio 2: nome della variablie non valido
123abc = "Invalid variable name"
print(123abc)
>>>
Input In [2]
123abc = "Invalid variable name"
^
SyntaxError: invalid syntax
Code language: JavaScript (javascript)
In questo secondo esempio, l’interprete Python ha sollevato un SyntaxError che ci dice che abbiamo scritto una sintassi non valida.
In effetti, non possiamo nominare le variabili come abbiamo fatto in questo esempio.
Lettura consigliata: Apprendimento Non Supervisionato in Python: Una Introduzione alle Tecniche di Clustering per Scoprire i Pattern
TypeError
In Python, si verifica un TypeError quando si esegue un’operazione o si utilizza una funzione con un oggetto di tipo inappropriato o incompatibile con essa.
Questo accade tipicamente quando si cerca di combinare oggetti o eseguire operazioni che non sono supportate dai loro tipi di dati.
Il TypeError è un’eccezione comune ed è utilizzato per rilevare problemi legati alle incompatibilità di tipo ed è un errore frequente quando si lavora con dati.
Vediamo un paio di esempi.
Esempio 1: sommare interi con stringhe
x = 5
y = "2"
z = x + y
>>>
TypeError Traceback (most recent call last)
Input In [3], in <cell line: 3>()
1 x = 5
2 y = "2"
----> 3 z = x + y
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Code language: JavaScript (javascript)
Questo esempio mostra che l’interprete Python ha trovato un errore nella riga 3. Questo è un TypeError perché stiamo provando a sommare un intero ( x = 5 ) con una stringa ( y = “2” ). Il che, ovviamente, non è possibile.
Esempio 2: concatenare liste ed interi
numbers = [1, 2, 3]
total = numbers + 4
>>>
TypeError Traceback (most recent call last)
Input In [4], in <cell line: 2>()
1 numbers = [1, 2, 3]
----> 2 total = numbers + 4
TypeError: can only concatenate list (not "int") to list
Code language: PHP (php)
In questo esempio, stiamo cercando di concatenare una lista (numbers) con un intero (4)
Sfortunatamente, la concatenazione non può essere fatta tra un intero ed una lista. Quindi, se vogliamo aggiungere il numero 4 alla lista numbers dobbiamo effettuare una operazione diversa.
ValueError
In Python, si verifica un ValueError quando un’operazione o una funzione riceve un argomento del tipo corretto ma con un valore inappropriato o non valido. Questo errore, quindi, indica che il valore fornito non è accettabile per l’operazione eseguita.
ValueError si presenta comunemente quando i dati non possono essere convertiti nel tipo previsto o quando una funzione riceve un argomento al di fuori dell’intervallo accettabile.
Esempio 1: tentativo di convertire una stringa in un intero
num_str = "abc"
num = int(num_str)
>>>
ValueError Traceback (most recent call last)
Input In [9], in <cell line: 2>()
1 num_str = "abc"
----> 2 num = int(num_str)
ValueError: invalid literal for int() with base 10: 'abc'
Code language: JavaScript (javascript)
L’interprete Python ha sollevato un ValueError in questo caso perché il metodo int() non può creare un intero dalla stringa che gli è stata passata come argomento.
Esempio 2: analisi di una stringa di data non valida
import datetime
date_str = "2023-20-30"
date = datetime.datetime.strptime(date_str, "%Y-%m-%d")
>>>
ValueError: time data '2023-20-40' does not match format '%Y-%m-%d'
Code language: JavaScript (javascript)
In questo caso, l’interprete Python solleva un ValueError perché la stringa contenente la data non corrisponde allo standard che abbiamo fornito (il mese numero 20 non esiste nel calendario!).
Lettura consigliata: Python e DataBricks: la giusta accoppiata per dominare i dati
IndexError
In Python, si verifica un IndexError quando si sta tentando di accedere a un indice o a un elemento di una sequenza (come una lista, una tupla o una stringa) utilizzando un indice che è fuori intervallo o quando non esiste.
Questo errore indica, in genere, che si sta tentando di accedere a un elemento in una posizione che non esiste all’interno della sequenza indicata.
Esempio: accesso all’indice di una lista fuori dal range
my_list = [1, 2, 3]
element = my_list[5]
>>>
IndexError Traceback (most recent call last)
Input In [1], in <cell line: 2>()
1 my_list = [1, 2, 3]
----> 2 element = my_list[5]
IndexError: list index out of range
Code language: HTML, XML (xml)
In questo esempio, l’interprete Python ci dice che c’è un IndexError nella riga 2.
Ciò è accaduto perché volevamo prendere il quinto elemento da una lista con solo tre elementi.
ZeroDivisionError
In Python, si verifica uno ZeroDivisionError quando si sta tentando di dividere un numero per zero.
La divisione per zero è matematicamente indefinita, e Python solleva questa eccezione per indicare che l’operazione non può essere eseguita.
Esempio: semplice divisione per zero
result = 5 / 0
>>>
ZeroDivisionError Traceback (most recent call last)
Input In [2], in <cell line: 1>()
----> 1 result = 5 / 0
ZeroDivisionError: division by zero
Code language: HTML, XML (xml)
In questo esempio, l’interprete Python ha sollevato uno ZeroDivisionError perché stiamo cercando di dividere un numero per zero, ma questo non è matematicamente possibile.
Gestione delle eccezioni
La gestione delle eccezioni è il primo passo per sviluppare un programma che non si interompa.
Come abbiamo detto in precedenza, l’obiettivo è quello di anticipare ciò che gli utenti potrebbero fare con il programma che abbiamo sviluppato.
Quindi, l’obiettivo è sollevare eccezioni, anticipando le azioni degli utenti, per evitare che il programma si interrompa.
Vediamo come farlo.
Lettura consigliata: Librerie Python per Data Science: una guida completa
Come utilizzare il blocco try e except
Il blocco try e except rappresenta una modalità tipica con la quale gli sviluppatori Python possono gestire le eccezioni.
Quando prevediamo che possa verificarsi un’eccezione, la solleviamo, avvisiamo l’utente e chiudiamo il programma. In questo modo gli utenti potranno eventualmente riprovare con un input diverso e corretto, oppure potranno chiedere supporto. Ma il programma si chiuderà e non verrà interrotto.
Vediamo un esempio e sviluppiamolo passo dopo passo per vederne le varie sfumature.
Immaginiamo, per esempio, di voler creare una funzione che restituisca la divisione di due numeri inseriti dagli utenti.
Possiamo scrivere il seguente programma:
# User writes numbers via CLI
num1 = input("Enter the first number: ")
num2 = input("Enter the second number: ")
try:
division = num1 / num2
print(division)
except:
print("something was wrong. Please: try again!")
Code language: PHP (php)
Supponiamo di chiamare questo programma division.py. Questo è il tipico esempio in cui diamo questo programma agli utenti e loro lo possono avviare con python3 division.py.
Nel programma che abbiamo scritto stiamo dicendo all’interprete Python le seguenti cose:
- Prova a fare la divisione tra i due numeri. Se ci riesci, stampa il risultato.
- Stampa “something was wrong. Please: try again!” se si verifica un errore.
Se lanciamo il programma e scegliamo di inserire i numeri 1 e 2 riceviamo:
something was wrong. Please: try again!
Code language: JavaScript (javascript)
Com’è possibile che qualcosa sia andato storto?? Il codice che abbiamo scritto è così semplice…
Ebbene, il primo errore è che non abbiamo informazioni su cosa sia andato storto.
Per migliorare il codice precedente, possiamo estendere il blocco delle eccezioni chiedendo all’interprete Python di dichiarare l’eccezione.
Possiamo farlo in questo modo:
# User writes numbers via CLI
num1 = input("Enter the first number: ")
num2 = input("Enter the second number: ")
try:
division = num1 / num2
print(division)
except Exception as e:
print("something was wrong. Please: try again!", e)
Code language: PHP (php)
Ora il blocco except è stato esteso e, se viene sollevata un’eccezione, la stampa in modo che noi possiamo effettuare ulteriori indagini.
Quindi, se lanciamo nuovamente il programma e scegliamo 1 e 2 otteniamo:
something was wrong. Please: try again! unsupported operand type(s) for /: 'str' and 'str'
Code language: JavaScript (javascript)
Ecco quello che è successo: il metodo input() inserisce delle stringhe. Quindi, anche se passiamo numeri interi, li trasforma in stringhe: ecco perché l’interprete Python non è riuscito a calcolare la divisione che volevamo.
Le stringhe possono essere trasformate in interi col metodo int() per come segue:
# User writes numbers via CLI
num1 = input("Enter the first number: ")
num2 = input("Enter the second number: ")
# Transform strings into integers
num1 = int(num1)
num2 = int(num2)
try:
division = num1 / num2
print(division)
except Exception as e:
print("something was wrong. Please: try again!", e)
Code language: PHP (php)
Ora, se scegliamo 1 e 2 il programma ci darà come risultato 0.5, come ci aspettavamo.
Quindi, se scegliamo num2 = 0 otteniamo l’eccezione prevista:
something was wrong. Please: try again! division by zero
Code language: JavaScript (javascript)
Conclusioni
Questo tutorial voleva essere un’introduzione alla gestione degli errori in Python utilizzando il blocco try e except che è un modo tipico di gestione gli errori.
Come abbiamo documentato, l’obiettivo che dobbiamo avere in mente è il modo in cui gli utenti potrebbero interrompere il programma che abbiamo scritto, e anticiparli.
Nel caso di cui abbiamo parlato, l’errore tipico che si può fare quando si divide è scegliere 0 come denominatore: poiché questo non è matematicamente possibile, dobbiamo dire all’interprete Python di sollevare un’eccezione affinché il programma non si interrompa.