Generare eccezioni

Completato

Ora che si ha una buona conoscenza dei traceback e della gestione delle eccezioni, si esaminerà la generazione delle eccezioni.

Si potrebbe già conoscere una situazione che potrebbe causare una condizione di errore durante la scrittura del codice. In queste situazioni, è utile generare eccezioni che consentono ad altri codici di comprendere qual è il problema.

La generazione di eccezioni può anche essere utile per il processo decisionale per altri codici. Come si è visto in precedenza, a seconda dell'errore, il codice può prendere decisioni intelligenti per risolvere, aggirare o ignorare un problema.

Gli astronauti limitano il consumo di acqua a circa 11 litri al giorno. Verrà creata ora una funzione che, a seconda del numero di astronauti, possa calcolare quanta acqua rimarrà dopo un giorno o più:

def water_left(astronauts, water_left, days_left):
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    return f"Total water left after {days_left} days is: {total_water_left} liters"

Provare con cinque astronauti, 100 litri di acqua rimasti e due giorni alla fine:

water_left(5, 100, 2)
'Total water left after 2 days is: -10 liters'

Non è molto utile, perché un deficit di litri dovrebbe essere un errore. Quindi, il sistema di navigazione potrebbe avvisare gli astronauti che non ci sarà abbastanza acqua per tutti in due giorni. Se si è un tecnico che sta programmando il sistema di navigazione, è possibile generare un'eccezione nella funzione water_left() per segnalare la condizione di errore:

def water_left(astronauts, water_left, days_left):
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    if total_water_left < 0:
        raise RuntimeError(f"There is not enough water for {astronauts} astronauts after {days_left} days!")
    return f"Total water left after {days_left} days is: {total_water_left} liters"

Ora eseguirla di nuovo:

water_left(5, 100, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in water_left
RuntimeError: There is not enough water for 5 astronauts after 2 days!

Nel sistema di navigazione, il codice per segnalare l'avviso può ora usare RuntimeError per avvisare:

try:
    water_left(5, 100, 2)
except RuntimeError as err:
    alert_navigation_system(err)

La funzione water_left() può anche essere aggiornata per evitare di passare tipi non supportati. Provare a passare argomenti che non siano interi per controllare l'output dell'errore:

water_left("3", "200", None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in water_left
TypeError: can't multiply sequence by non-int of type 'NoneType'

L'errore da TypeError non è molto descrittivo nel contesto di ciò che la funzione prevede. Aggiornare la funzione in modo che usi TypeError ma con un messaggio migliore:

def water_left(astronauts, water_left, days_left):
    for argument in [astronauts, water_left, days_left]:
        try:
            # If argument is an int, the following operation will work
            argument / 10
        except TypeError:
            # TypeError will be raised only if it isn't the right type 
            # Raise the same exception but with a better error message
            raise TypeError(f"All arguments must be of type int, but received: '{argument}'")
    daily_usage = astronauts * 11
    total_usage = daily_usage * days_left
    total_water_left = water_left - total_usage
    if total_water_left < 0:
        raise RuntimeError(f"There is not enough water for {astronauts} astronauts after {days_left} days!")
    return f"Total water left after {days_left} days is: {total_water_left} liters"

Riprovare per ottenere un errore migliore:

water_left("3", "200", None)
Traceback (most recent call last):
  File "<stdin>", line 5, in water_left
TypeError: unsupported operand type(s) for /: 'str' and 'int'

During handling of the preceding exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in water_left
TypeError: All arguments must be of type int, but received: '3'