Sistemi software ove parti del programma reagiscono ad eventi o si scambiano messaggi sono molto comuni, fra questi abbiamo:
- le interfacce grafiche (agiscono in funzione di messaggi da tastiera e mouse)
- gli applicativi di rete (dalla rete arrivano richieste di connessione)
- i desktop utilizzano messaggi per far parlare le varie componenti (D-bus, usato in Linux da kde4 e gnome)
- il sistema operativo utilizza messaggi per far comunicare diverse parti o comunicare con programmi utente.
Un particolare utilizzo di questa tecnica puo' essere considerato il sistema che gestisce, in molti linguaggi, il verificarsi di errori: una routine, in cui si verifica un errore, lancia un'"eccezione", che e' un messaggio che puo' essere gestito dal programma stesso o puo' causare l'arresto del programma. Questo sistema puo' anche essere usato come sistema generale di comunicazione fra le diverse parti del programma.
Python usa, per le eccezioni, seguenti istruzioni:
try : delimita un blocco entro cui sono intercettate eccezioni raise : genera un'eccezione, ed interrompe l'esecuzione assert: genera un'eccezione, di tipo "AssertionError", in base a condizioni except: intercetta l'eccezione e definisce un blocco di istruzioni da eseguire per una una data eccezione.
L'eccezione lanciata da"raise" puo' essere:
- l'istanza di una classe;
- una classe, ed in questo caso raise la istanzia automaticamente,
- un'eccezione precedente, che viene rilanciata. Rilanciare un'eccezione puo' servire in strutture try-except annidate, per passare l'eccezione ad un blocco superiore nella gerarchia.
Sintassi dell'istruzione "raise":
raise nomeistanza raise nomeclasse raise raise nomeclasse from altraclass
In versioni di Python precedenti alla 2.6 le eccezione potevano anche essere semplici stringhe. Ma in Python 3, sono sempre clessi, che ereditano la classe Exception;
Dopo l'istruzione "try", che identifica il blocco entro cui verificare se sono state lanciate eccezioni, ci sono uno o piu'blocchi individuati da istruzioni "except" , che sono eseguiti se si verificano eccezioni di un certo tipo. La sintassi e' la seguente:
try: ... ... ... # blocco entro cui puo' verificarsi l'eccezione ... # Es.: if a<0: raise ecc1 ... except ecc1 as var : ... ... # blocco eseguito per l'eccezione: ecc1 ... except (ecc2,ecc3) as var : ... ... # blocco eseguito per le eccezioni: ecc2 od ecc3 ... except ... ... # blocco per tutte le altre eccezioni ... else: ... ... # blocco eseguito se NON ci sono eccezioni ... finally: ... ... # blocco eseguito in ogni caso ...
I blocchi che seguono le istruzioni "except" vengono eseguiti se l'eccezione corrisponde al nome della classe che segue except (qui: ecc1,ecc2,ecc3). Il nome che conclude la linea con "except" (qui: var) e' un riferimento che punta all'eccezione, che puo' essere usato nel blocco except.
Il blocco else e' eseguito se non e' sollevata un'eccezione.
Il blocco finally viene eseguito in ogni caso, sia se ci sono eccezioni sia se l'esecuzione del blocco try non ne genera. In blocco "finally", e' opzionale e serve ad assicurarsi che certe operazioni siano eseguite in ogni caso, anche se si verificano errori
Esempio:
class Ecc(Exception): # definizione di una eccezione nome="eccezione Ecc" class Ecc1(Exception): # definizione di un'altra eccezione nome="eccezione Ecc1" def eccezsub(a): if a<2 : raise Ecc # lancia eccezioni se a<=2 if a==2 : raise Ecc1 print "OK, valore grande:",a for i in [1,2,3] : print "provo il numero:",i try: eccezsub(i) # blocco try, da testare print "OK niente eccezione" except Ecc as X : # cattura eccezione : Ecc print "valoretroppo piccolo:",X.nome except Ecc1 as X : # cattura eccezione : Ecc1 print "valore ma quasi OK:",X.nome else: print " niente eccezione, valore OK" finally: print "valore verificato:",i print "fine delle prove"
Se viene lanciata un'eccezione che non corrisponde ad un except questa non viene catturata dal try, ma dal Python, che interrompe il programma. Il blocco finally viene eseguito prima dell'interruzione.
In Python 3 raise puo' avere anche una sintassi del tipo:
except eccezione as E .... raise nuova_eccezione from E
L'eccezione: "E" finisce nell'attributo "__cause__" della nuova eccezione lanciata. In questo modo si puo' avere una gerarchia di eccezioni, che sono elencate nel messaggio di errore.
In caso di errori durante l'esecuzione del programma, Python genera eccezioni, alcune di queste sono elencate nella tabella seguente:
ZeroDivisionError : divisione per zero; Es.: 3/0 OverflowError : valore troppo grande Es.: 10.0**1000 IndexError : indice sbagliato Es.: a=[1,2,3] ; a[8] IOError : errore di input/output ImportError : non si trova il file da importare KeyError : chiavi di dizionario inesistente Es.: d={'a':1} ; d['b'] TypeError : operazione non permessa su certi dati (Es.: elevazione a potenza con stringhe)
Esempio di come si possano utilizzare le istruzioni try ed except in modo da evitare che un certo errore provochi l'interruzione del programma:
a=2.0 for i in [1,0] : print "divido per:",i try: b=a/i except ZeroDivisionError : print "sto dividendo per zero, metto -1" b=-1 finally: print "valore di b:",b print "fine delle prove"
L'istruzione "assert" verifica una condizione e, se non e' vera, lancia l'eccezione "AssertionError". Viene usata nella fase di test dei programmi. Se Python viene chiamato con l'opzione di ottimizzazione '-O' , la variabile interna __debug__ e' False e l'istruzione assert non ha effetto.
Esempio:
assert 5 > 2 : questa NON lancia un'eccezione assert 2 > 5 , "messaggio" : la condizione e' falsa, viene lanciata un'eccezione che contiene il messaggio (opzionale)