Corso Python / Gestire le eccezioni con try, except, else, finally e raise

La gestione delle eccezioni permette di distinguere un errore previsto da un guasto che deve essere segnalato al chiamante. Mantieni piccolo il blocco try e intercetta solo i problemi per cui sai definire una risposta: proseguire dopo aver nascosto un errore non equivale a recuperare correttamente.

Capire i percorsi di try, except, else e finally

Le istruzioni nel blocco try vengono eseguite in ordine. Se una solleva un’eccezione, le successive nel try vengono saltate e Python cerca il primo except compatibile. Un errore di un tipo non gestito continua verso il chiamante; se nessuno lo intercetta, il programma termina segnalando l’errore.

Else viene eseguito quando il blocco try termina senza eccezioni. Puoi mettervi il lavoro che dipende da un risultato valido, evitando di includerlo inutilmente nel tratto protetto. Un’eccezione sollevata dentro else non viene catturata dagli except dello stesso try.

Finally serve per operazioni di chiusura e pulizia sia nel percorso normale sia durante la propagazione di un’eccezione. Non è un annullamento delle modifiche: come nel caso dei file aperti con with, chiudere una risorsa non ripristina necessariamente il suo contenuto. Evita return dentro finally, perché può sostituire un valore di ritorno o sopprimere un errore pendente.

Con raise segnali esplicitamente una violazione delle regole. La guida ufficiale a errori ed eccezioni descrive selezione dei gestori, propagazione e collegamento delle cause.

Eseguire un esempio con importi validi e non validi

Salva il codice completo come exceptions_demo.py, in UTF-8, in una cartella di esercizio. Esegui python exceptions_demo.py nel terminale con l’interprete del progetto selezionato. Fuori dall’ambiente virtuale su macOS o Ubuntu usa python3 se è il comando dell’interprete scelto. L’esempio non richiede file di dati né librerie esterne.

def parse_amount(text):
    amount = int(text)
    if amount < 0:
        raise ValueError("amount must be nonnegative")
    return amount

for text in ["8000", "abc", "-1"]:
    try:
        amount = parse_amount(text)
    except ValueError as error:
        print("invalid:", text)
    else:
        print("accepted:", amount)
    finally:
        print("checked")

Il risultato previsto è:

accepted: 8000
checked
invalid: abc
checked
invalid: -1
checked

8000 supera sia la conversione int sia il controllo del segno: viene stampato accepted nel blocco else. Abc fallisce durante la conversione, mentre -1 viene convertito ma viola la regola nonnegative, cioè «non negativo». Entrambi i problemi sono ValueError e raggiungono il gestore previsto.

Checked compare una volta per ciascun tentativo, compresi quelli non validi. In questo programma è solo un messaggio che rende visibile finally; non indica che l’importo sia stato accettato e non chiude risorse, perché qui non ne sono state aperte. Dopo la gestione del singolo ValueError, il ciclo continua con il dato successivo.

Scegliere cosa recuperare e cosa propagare

La funzione promette di analizzare un testo: int può sollevare ValueError se quel testo non descrive un intero. Se passi un oggetto non previsto, per esempio None, puoi invece ottenere TypeError; l’except dell’esempio non lo nasconde. L’assenza di una gestione universale è utile per rendere visibili gli errori di programmazione o di contratto.

except Exception: pass elimina la segnalazione di molti guasti e può far proseguire il programma con uno stato incompleto. Intercetta un errore vicino al punto in cui puoi decidere come recuperare: chiedere un nuovo valore è diverso dal fingere che una scrittura su disco sia riuscita. Se più gestori sono necessari, metti quelli specifici prima di quelli generali compatibili.

Un return nel gestore fornisce un risultato al chiamante, che continua come dopo un ritorno normale. Un raise senza argomenti dentro except rilancia invece l’eccezione corrente. Se devi presentare un errore applicativo diverso conservando la causa, usa una forma come raise RuntimeError("conversion failed") from error, adattando tipo e messaggio all’interfaccia.

Per i file e altre risorse che lo supportano, preferisci with. Se gestisci la pulizia manualmente in finally, considera che l’apertura può essere fallita prima di assegnare la risorsa: non tentare di chiudere un oggetto mai creato. Infine, la gestione delle eccezioni non garantisce pulizia dopo una terminazione forzata del processo o un’interruzione del sistema.

Confrontare accettazione, rifiuto e cause

  1. Aggiungi "0" all’elenco dei testi e verifica accepted: 0 seguito da checked. La regola rifiuta i negativi ma consente zero.
  2. Aggiungi una stringa vuota "". Deve entrare nel gestore ValueError durante la conversione e stampare invalid seguito da checked.
  3. Solo su questi dati fissi di prova, sostituisci temporaneamente la stampa nel gestore con una stampa del tipo e del testo di error. Confronta il messaggio generato da int per abc con amount must be nonnegative generato da raise per -1.
  4. Ripristina il sorgente originale e confronta le sei righe con l’output previsto.

Il messaggio sintetico invalid è intenzionalmente comune a due cause differenti: può bastare per il flusso della prova, ma un’interfaccia reale dovrebbe offrire indicazioni utili alla correzione. I dettagli diagnostici vanno selezionati in base al destinatario: non copiare indiscriminatamente eccezioni che potrebbero contenere token, credenziali o dati personali nei messaggi pubblici o nei log.