Python Try...Except
Impara i blocchi try, except, else e finally di Python con esempi chiari. Gestisci ZeroDivisionError, ValueError, FileNotFoundError e altro.
Quando Python incontra un errore in fase di esecuzione, solleva un'eccezione — un oggetto che rappresenta cosa è andato storto. Senza alcuna gestione, un'eccezione termina immediatamente il programma. L'istruzione try...except consente di intercettare queste eccezioni, gestirle in modo elegante e mantenere il programma in esecuzione.
Questo capitolo tratta:
- Il blocco
try/except— intercettare un'eccezione specifica - Acquisire i dettagli dell'eccezione con
as - Più clausole
excepte l'intercettazione di più eccezioni contemporaneamente - Il blocco
else— codice che viene eseguito solo quando non si verifica alcuna eccezione - Il blocco
finally— codice di pulizia che viene sempre eseguito - Eccezioni predefinite comuni e quando si verificano
- Ri-sollevare eccezioni
- Buone pratiche ed errori comuni
Il blocco try...except di base
Racchiudi il codice che potrebbe fallire in un blocco try. Se Python solleva un'eccezione, l'esecuzione salta al blocco except corrispondente invece di andare in crash.
Output:
Error: division by zeroPython tenta la divisione, solleva ZeroDivisionError e il blocco except la gestisce. La clausola as e associa l'oggetto eccezione a e in modo da poter ispezionare o registrare il messaggio.
Se il blocco try ha successo, il blocco except viene saltato completamente.
Intercettare un tipo di eccezione specifico
Specifica sempre il tipo di eccezione che ti aspetti. Intercettare un tipo con nome rende chiara la tua intenzione ed evita di nascondere accidentalmente bug non correlati.
try:
number = int("abc")
except ValueError as e:
print(f"Could not convert: {e}")Output:
Could not convert: invalid literal for int() with base 10: 'abc'int("abc") solleva ValueError perché "abc" non è un intero valido. Specificare ValueError nella clausola except significa che qualsiasi altra eccezione imprevista si propagerà e verrà mostrata come errore invece di essere inghiottita silenziosamente.
Più clausole except
Un singolo blocco try può avere più clausole except — Python le controlla dall'alto verso il basso ed esegue la prima corrispondente.
def safe_index(items, index):
try:
return items[index]
except IndexError:
print("Index out of range.")
except TypeError:
print("Index must be an integer.")
safe_index([1, 2, 3], 10) # IndexError
safe_index([1, 2, 3], "a") # TypeErrorOutput:
Index out of range.
Index must be an integer.L'ordine è importante: inserisci i tipi di eccezione più specifici prima di quelli più generali, in modo che il gestore specifico venga eseguito per primo.
Intercettare più eccezioni in una sola clausola
Quando due o più eccezioni richiedono la stessa risposta, raggruppale in una tupla:
try:
value = int("not-a-number")
except (ValueError, TypeError) as e:
print(f"Input error: {e}")Output:
Input error: invalid literal for int() with base 10: 'not-a-number'Il blocco else
Il blocco else viene eseguito solo quando il blocco try si completa senza sollevare alcuna eccezione. Usalo per il codice che deve essere eseguito in caso di successo ma che non necessita di protezione dagli errori:
try:
result = 10 / 2
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print(f"Division succeeded. Result: {result}")Output:
Division succeeded. Result: 5.0Mantenere la logica del percorso di successo in else (anziché in fondo al blocco try) evita di intercettare accidentalmente le eccezioni che il codice del percorso di successo stesso potrebbe sollevare.
Il blocco finally
Il blocco finally viene eseguito in ogni caso — sia che il blocco try abbia successo, sia che un'eccezione venga intercettata, sia che un'eccezione si propaghi non intercettata. Usalo per la pulizia: chiudere file, rilasciare blocchi o disconnettersi da un database.
try:
result = 10 / 0
except ZeroDivisionError:
print("Error caught.")
finally:
print("This always runs — cleanup goes here.")Output:
Error caught.
This always runs — cleanup goes here.Anche se commenti il blocco except, finally viene comunque eseguito prima che Python propaghi l'eccezione.
Struttura completa in sintesi
try:
# code that may raise an exception
except SomeException as e:
# handle the exception
except (AnotherError, YetAnother):
# handle either of these
else:
# runs only when try succeeded
finally:
# always runsEccezioni predefinite comuni
| Eccezione | Quando si verifica |
|---|---|
ZeroDivisionError | Divisione o modulo per zero |
ValueError | Tipo corretto, valore sbagliato (es. int("abc")) |
TypeError | Operazione applicata al tipo sbagliato |
IndexError | Indice della sequenza fuori intervallo |
KeyError | Chiave del dizionario non trovata |
FileNotFoundError | File o directory non esiste |
AttributeError | L'oggetto non ha tale attributo |
ImportError | Il modulo non può essere importato |
NameError | Il nome della variabile non è definito |
Tutte queste ereditano dalla classe base Exception. Puoi intercettare Exception per gestirle tutte in una sola clausola, ma preferisci tipi specifici ove possibile.
Gestire un file mancante
try:
with open("data.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("Error: File not found.")Output (quando data.txt non esiste):
Error: File not found.Ri-sollevare un'eccezione
A volte vuoi registrare un'eccezione o eseguire una pulizia parziale, ma lasciare comunque che si propaghi al chiamante. Usa un raise semplice all'interno di un blocco except:
def process():
try:
result = 10 / 0
except ZeroDivisionError:
print("Logging error...")
raise # re-raises the original ZeroDivisionError
try:
process()
except ZeroDivisionError as e:
print(f"Outer handler caught: {e}")Output:
Logging error...
Outer handler caught: division by zeroIl raise semplice preserva il traceback originale, in modo che il debug non sia più difficile rispetto a senza il gestore.
Intercettare la classe base Exception
Puoi usare Exception come catch-all per qualsiasi errore che non termina il sistema:
try:
result = 10 / 0
except Exception as e:
print(f"An error occurred: {type(e).__name__}: {e}")Output:
An error occurred: ZeroDivisionError: division by zerotype(e).__name__ fornisce il nome specifico della classe anche quando si intercetta tramite la classe base. Questo è utile nei gestori di livello superiore dove si desidera registrare ogni errore imprevisto.
Bare except — e perché evitarlo
Un bare except (senza tipo di eccezione) intercetta letteralmente tutto, inclusi KeyboardInterrupt (Ctrl+C) e SystemExit, rendendo difficile interrompere il programma:
# Avoid this pattern
try:
result = 10 / 0
except:
print("Some error occurred")Preferisci except Exception se hai bisogno di un'intercettazione ampia, perché lascia comunque che KeyboardInterrupt e SystemExit si propaghino normalmente.
Buone pratiche
- Sii specifico. Intercetta il tipo di eccezione più ristretto che abbia senso.
- Non silenziare le eccezioni. Come minimo registra l'errore; non lasciare mai un blocco
exceptvuoto. - Usa
elseper il codice del percorso di successo. Questo mantiene il bloccotryil più piccolo possibile. - Usa
finallyper la pulizia. Oppure, ancora meglio, usa un'istruzionewith— vedi Python with Statement. - Evita il
bare except. Usaexcept Exceptionse hai bisogno di un'intercettazione ampia. - Ri-solleva quando appropriato. Se non puoi gestire completamente l'errore, lascia che si propaghi con
raise.
Per sollevare eccezioni deliberatamente e creare le proprie classi di eccezione, vedi Python raise and Custom Exceptions.