Salta al contenuto
Note per Studenti Eccezioni in Python

Eccezioni in Python

In questa pagina 6

Che cosa sono

Un'eccezione è un oggetto che segnala un errore a tempo di esecuzione. Quando viene sollevata, l'esecuzione normale si interrompe e l'eccezione risale lo stack delle chiamate finché qualcuno la gestisce; se nessuno la gestisce il programma termina stampando il traceback (sequenza delle chiamate, dalla più esterna alla riga dell'errore, e in fondo tipo e messaggio).

Traceback (most recent call last):
  File "prog.py", line 5, in <module>
    print(media([]))
  File "prog.py", line 2, in media
    return sum(v) / len(v)
ZeroDivisionError: division by zero

Si legge dal fondo: tipo dell'eccezione, poi la riga che l'ha provocata, poi chi l'ha chiamata.

Eccezioni comuni

Eccezione Causa tipica
NameError nome non definito (refuso, variabile non ancora assegnata)
TypeError operazione su un tipo sbagliato ("a" + 1, None + 1, numero di argomenti errato)
ValueError tipo giusto, valore inammissibile (int("abc"))
IndexError indice fuori dalla sequenza
KeyError chiave assente in un dizionario
ZeroDivisionError divisione o modulo per zero
AttributeError metodo o attributo inesistente (lista.push(1))
FileNotFoundError file da leggere inesistente
RecursionError ricorsione troppo profonda (vedi RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →)

try / except

python
try:
    n = int(input("Numero: "))
    print(100 / n)
except ValueError:
    print("non è un intero")
except ZeroDivisionError:
    print("zero non ammesso")

Forme complete:

python
try:
    f = open(nome, encoding="utf-8")
except FileNotFoundError as e:       # "as e": l'oggetto eccezione
    print("Impossibile aprire:", e)
except (ValueError, TypeError):      # più tipi nello stesso ramo
    ...
else:
    # eseguito solo se nel try non c'è stata eccezione
    dati = f.read()
    f.close()
finally:
    # eseguito sempre (con o senza eccezione): rilascio di risorse
    print("fine")
  • I rami except vengono provati in ordine: il primo compatibile vince.
  • Nel try mettere solo le istruzioni che possono sollevare l'eccezione attesa.
  • except Exception: cattura quasi tutto: va usato solo con cognizione (es. per registrare l'errore e rilanciarlo). Un except: nudo nasconde anche i bug.

Sollevare eccezioni: raise

Una funzione che riceve argomenti non validi non deve stampare un messaggio e proseguire: deve segnalare l'errore al chiamante.

python
def media(valori):
    if len(valori) == 0:
        raise ValueError("media di una lista vuota")
    return sum(valori) / len(valori)

Il chiamante decide se gestirla. raise da solo dentro un except rilancia l'eccezione corrente.

Eccezioni o controlli preventivi

Due stili:

python
# controllo prima (LBYL: look before you leap)
if chiave in d:
    v = d[chiave]

# prova e gestisci (EAFP: easier to ask forgiveness than permission)
try:
    v = d[chiave]
except KeyError:
    v = 0

In Python entrambi sono accettati; EAFP è naturale quando il caso d'errore è raro o il controllo preventivo è difficile (input dell'utente, file). In C non ci sono eccezioni: gli errori si segnalano con valori di ritorno (vedi Input e output in Cprintf con larghezza e precisione, scanf con indirizzi e valore di ritorno, lettura di righe con fgets, file con FILE*, fopen, fclose, fprintf, fscanf, controllo degli errori e della fine del file.Input e output in C →).

Errori tipici

  • except: vuoto o except Exception: pass, che nasconde gli errori veri.
  • Blocco try troppo grande: si catturano eccezioni di righe che non c'entrano.
  • Usare le eccezioni al posto di un normale if per casi frequenti e prevedibili.

Teoria collegata