<aside> <img src="notion://custom_emoji/899aa73c-260f-813c-baa9-0003a270e9e6/29eaa73c-260f-8057-931f-007a0ac7402b" alt="notion://custom_emoji/899aa73c-260f-813c-baa9-0003a270e9e6/29eaa73c-260f-8057-931f-007a0ac7402b" width="40px" />
Indice
</aside>
Nella programmazione, non tutto va sempre come previsto. Una 'eccezione' è un evento anomalo, un errore, che si verifica durante l'esecuzione (cioè a run-time) di un programma e che ne interrompe il normale flusso.
In Python, quando si verifica un errore, il programma “solleva” (in inglese raises) un'eccezione. Se questa eccezione non viene “catturata” (ovvero gestita), il programma si arresta bruscamente (va in crash) mostrando un messaggio di errore (il traceback).
flowchart LR
A([Errore si verifica]) --> B([raise Eccezione])
B --> C{{Eccezione gestita?}}
C -->|Sì| D([Codice di gestione<br> dell'errore])
C -->|No| E[[Crash del programma]]
E --> F[/Stampa del traceback/]
%% Definizione degli stili
classDef error fill:#ffcccc,stroke:#cc0000,stroke-width:2px;
classDef raise fill:#ffe6cc,stroke:#ff6600,stroke-width:2px;
classDef decision fill:#fff2b3,stroke:#aa8800,stroke-width:2px;
classDef handler fill:#ccffcc,stroke:#009900,stroke-width:2px;
classDef crash fill:#ffd6d6,stroke:#ff0000,stroke-width:2px;
classDef trace fill:#f2f2f2,stroke:#666666,stroke-width:1px;
%% Assegnazione degli stili
class A error;
class B raise;
class C decision;
class D handler;
class E crash;
class F trace;
<aside> ✨
Nei linguaggi più vecchi si usavano codici fittizi come -1 o False per indicare una condizione errore, ad esempio come valori restituiti da una funzione.
Questo modo di programmare mescola il “risultato” della funzione e lo “stato d’errore”, rendendo difficile capire dove sia avvenuto il problema.
Con le eccezioni, il flusso logico e quello d’errore sono separati e più leggibili.
</aside>
Immaginiamo, nel nostro progetto “Code Combat”, di aver aggiunto alla classe Player un metodo per calcolare il danno medio inflitto durante il combattimento.
Per farlo, la classe tiene traccia di self.__total_damage_dealt (danno totale inflitto) e self.__total_hits (numero di colpi andati a segno).
Il metodo potrebbe essere scritto così:
# Dentro la classe Player
def get_average_damage(self):
# Calcola la media: danno totale / numero di colpi
return self.__total_damage_dealt / self.__total_hits
Cosa succede se chiamiamo questo metodo su un giocatore che è appena entrato nell’arena e non ha ancora sferrato nessun colpo?
gimli = Player("Gimli", 50, 18, 7)
# gimli non ha ancora combattuto, quindi self.__total_hits è 0
print("Calcolo del danno medio di Gimli...")
media = gimli.get_average_damage() # CRASH!
print(f"Danno medio: {media}")
Nel momento in cui il metodo esegue la divisione, self.__total_hits è 0. Python non può dividere un numero per zero e solleva un’eccezione ZeroDivisionError, arrestando l’intero programma.
Dato che non abbiamo fornito nessun gestore di eccezioni, l’eccezione viene catturata dal gestore di default di Python, che visualizza un “traceback” e termina il programma.
Questo è l'output dell'eccezione:
Traceback (most recent call last):
File "main.py", line 16, in <module>
media = gimli.get_average_damage() # CRASH!
File "main.py", line 10, in get_average_damage
return self.__total_damage_dealt / self.__total_hits
~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
ZeroDivisionError: division by zero
Notate come il traceback mostri l'intera catena di chiamate (lo stack):
Divisione.py, riga 4, dentro la funzione dividi.