|
Durante l'esecuzione dei programmi possono insorgere degli
errori. Linguaggi tradizionali come il C o il Pascal non prendono
particolari provvedimenti per gestirli (è compito del
programmatore "stare attento" e verificare bene i valori ritornati per
riconoscere e gestire gli errori). In pratica però una condizione
di errore è sempre qualcosa di particolare che altera il normale
flusso del programma, e che tende a sfuggire all'attenzione del
programmatore. Queste condizioni eccezionali, in pratica
così eccezionali non sono, e devono essere in qualche modo
gestite perché i programmi diventino robusti (ovvero non si
piantano ogni 5 minuti). Non a caso i "disastri a catena" che
avvengono nei programmi hanno origine in qualche errore non gestito
che si propaga causando errori su errori fino alla terminazione del
programma (nei casi fortunati) o il blocco del sistema (di
solito). Per gestire le condizioni di errore è stato inventato,
fin dai tempi antichi dell'informatica (vent'anni fa), il meccanismo
delle eccezioni. Per molto tempo però la gestione delle eccezioni
è rimasta confinata ai linguaggi assembler: da qualche anno
è approdata anche ai linguaggi ad alto livello, come C++, Java e,
appunto, Python.
Vediamo come funzionano le eccezioni considerando la gestione di
un errore tanto semplice quanto (spesso) inaspettato: una divisione
per zero:
>>> x = 0
>>>
>>> 1/x
Traceback (innermost last):
File "<stdin>" , line 0, in ?
ZeroDivisionError: integer division or modulo
>>> x = 0
>>> try:
... y = 1/x
... except ZeroDivisionError:
... print "cannot divide!"
...
cannot divide!
In caso di eccezione, l'esecuzione si interrompe e
causa la stampa di un messaggio di errore. Per essere esatti,
durante l'esecuzione di un programma una eccezione causa il ritorno
dalla funzione o metodo in cui ci si trova e la generazione di un
eccezione nel punto in cui si ritorna. Questo meccanismo viene
ripetuto, portando ad un ritorno forzato da tutte le chiamate
correnti finché non si raggiunge il toplevel (che causa la
terminazione con un messaggio errore), a meno che l'eccezione non
venga in qualche modo catturata e gestita. In questo modo le
condizioni di errore non sono ignorabili a meno che il
programmatore non decida esplicitamente di ignorarle. Comunque in
questo mod si tende a confinare gli errori in precisi sottosistemi
che non causano la terminazione anomala del programma. Nell'esempio
vediamo anche come funziona il meccanismo di gestione delle
eccezioni: si sottopone a controllo il blocco che può causare
l'eccezione tramite try. Le eccezioni possono essere
provocate esplicitamente utilizzando raise. Se scatta una
eccezione, questa viene confrontata con le possibili eccezioni che
si vogliono gestire, usando la clausola except. Non ci deve
essere necessariamente una sola clausola except ma anzi
è utile che ce ne sia più d'una. Scatterà quella
corrispondente al tipo di eccezione sollevato. Se nessuna va bene,
l'eccezione si propaga come se non ci fosse stato alcun controllo.
Questa procedura è anche il modo più corretto di
gestirla: controllare gli errori che si prevedono e lasciar passare
quelli inaspettati in modo che in fase di test e debug si possano
rilevare le condizioni di errore non previste. Accenniamo infine al
fatto che in Python la try può anche avere le clausole
else e finally. La else viene eseguita se il
codice sottoposto a try non causa eccezioni. La clausola
finally invece viene eseguita in ogni caso, sia nel caso che
il codice causi eccezioni, sia nel caso che tutti fili liscio.
È anche possibile utilizzare classi definite dall'utente per
organizzare gerarchicamente le eccezioni. Anche questo non lo
trattiamo in dettaglio per motivi di spazio.
|