Menu

Eccezioni in Python: try, except, else, finally e raise

Come gestire gli errori in Python: try/except/finally, catturare eccezioni specifiche, sollevarne di tue e quando lasciar propagare un errore.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Gli errori sono solo valori con un brutto carattere

Quando qualcosa va storto in Python (una divisione per zero, la lettura di un file mancante, l'interpretazione di un numero non valido) l'interprete crea un oggetto eccezione e inizia a risalire lo stack delle chiamate finché qualcosa non lo cattura. Se niente lo cattura, il programma termina e stampa un traceback.

Le eccezioni di per sé non sono un male. Sono il modo in cui Python segnala "non posso continuare con questa operazione, ecco perché". Il tuo compito è decidere, caso per caso, da quali sai come riprenderti e quali invece devi lasciar salire.

La forma di base

  • try: apre il blocco di codice rischioso.
  • except ValueError: cattura quella specifica eccezione se viene sollevata dentro il try.
  • Se non scatta nessuna eccezione, l'except viene saltato del tutto.

Esegui lo snippet con 42: funziona. Eseguilo con hello: parte il gestore.

Catturare eccezioni specifiche

Python ha una gerarchia di tipi di eccezione. Eccone alcune che incontrerai spesso:

  • ValueError: un valore era sbagliato in qualche modo (int("abc"), argomenti fuori intervallo).
  • TypeError: è stato usato il tipo sbagliato ("hi" + 3).
  • KeyError: una chiave di un dict non è stata trovata.
  • IndexError: un indice di una sequenza era fuori intervallo.
  • FileNotFoundError: un file non esiste.
  • ZeroDivisionError: si è tentato di dividere per zero.
  • AttributeError: un oggetto non ha l'attributo richiesto.

Cattura quella specifica che sai gestire:

Puoi catturare più eccezioni in una sola clausola passando una tupla:

Nota as e. Associa l'oggetto eccezione a e, così puoi esaminarne il messaggio o gli attributi.

Evita di catturare tutto

Un except: generico cattura letteralmente qualsiasi cosa, compresi KeyboardInterrupt (il tuo Ctrl-C) e le uscite a livello di sistema. Non usarlo.

except Exception: è un po' meglio, ma resta pericoloso: inghiotte bug che non avevi previsto e nasconde la vera origine dei problemi:

# Non farlo senza un ottimo motivo.
try:
    do_something()
except Exception:
    pass

La mossa giusta è quasi sempre catturare l'eccezione specifica da cui sai come riprenderti. Se un'eccezione inattesa arriva in cima al programma, il traceback ti dice esattamente cosa è andato storto: è una funzionalità, non un difetto.

else e finally

L'istruzione try ha altre due clausole facoltative:

  • else viene eseguito se il blocco try è terminato senza sollevare eccezioni.
  • finally viene eseguito in ogni caso, eccezione o no.

else è il posto più pulito per il codice "solo in caso di successo": non vuoi che un blocco try faccia più della parte che può davvero fallire. finally serve per la pulizia che deve avvenire anche se il try fallisce: chiudere una risorsa, rilasciare un lock, ripristinare uno stato.

Sollevare eccezioni tu stesso

Usa raise per segnalare un errore nel tuo codice:

Scegli un tipo di eccezione adatto a ciò che è andato storto. Prima di definire una tua classe, prova con quelli integrati: ValueError, TypeError, FileNotFoundError.

Definire un'eccezione personalizzata

Quando quelle integrate non esprimono il significato giusto, definisci un'eccezione personalizzata:

Eredita da Exception (o da un'eccezione integrata più specifica) e dai alla classe una docstring. Di solito non serve altro. Le eccezioni personalizzate permettono a chi chiama di catturare solo l'errore che ha senso nel suo dominio.

raise ... from ...: eccezioni concatenate

Quando un'eccezione ne scatena un'altra, conserva la catena:

from e collega l'errore originale. Quando stampa il traceback, Python li mostra entrambi: il ConfigError emerso in superficie e il FileNotFoundError che l'ha causato. Una traccia del genere è preziosissima durante il debug.

Context manager: il modo più pulito per fare pulizia

finally va bene, ma per risorse come i file un context manager (quello che usa with) è quasi sempre meglio:

# versione con finally
f = open("data.txt")
try:
    data = f.read()
finally:
    f.close()

# versione con with
with open("data.txt") as f:
    data = f.read()

Entrambe sono sicure. La forma con with è più breve e si applica da sola. Ricorri a finally solo quando stai facendo qualcosa che la libreria standard non avvolge già in un context manager.

Quando non catturare

Catturare un'eccezione è una decisione: stai dicendo "questo lo so gestire". Se non sai gestirla, lasciala propagare. Codice come questo è quasi sempre un errore:

try:
    do_work()
except Exception:
    pass  # ignora tutto in silenzio

Zittire gli errori rende invisibili i bug. Meglio un crash rumoroso che andare avanti zoppicando in uno stato incoerente.

Riassumendo

  • try/except ti permette di gestire gli errori da cui puoi riprenderti.
  • Cattura eccezioni specifiche, non Exception alla cieca.
  • raise segnala errori nel tuo codice.
  • I blocchi with sostituiscono la maggior parte della pulizia con finally.
  • Nel dubbio, lascia propagare l'eccezione.

Prossimo argomento: un giro tra gli errori specifici che Python solleva più spesso (KeyError, ValueError, ModuleNotFoundError e qualche altro) più le abitudini di debug che li risolvono in fretta.

Domande frequenti

Come gestisco gli errori in Python?

Metti il codice rischioso in un blocco try e cattura l'eccezione specifica in un blocco except: try: risky() except ValueError: .... Il blocco facoltativo else viene eseguito se non si è verificata nessuna eccezione; finally viene eseguito in ogni caso, per la pulizia.

Conviene catturare Exception per stare tranquilli?

No. Un except: generico o un except Exception: nascondono bug che non avevi previsto. Cattura l'eccezione specifica da cui sai come riprenderti, e lascia propagare tutto il resto così vedi il vero problema.

Qual è la differenza tra raise e raise from?

raise NewError(...) solleva una nuova eccezione. raise NewError(...) from original mantiene collegata l'eccezione originale come causa, e Python la mostra nel traceback. Usa from quando un errore di basso livello ha scatenato un errore di livello più alto che vuoi far emergere.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA