Skapa undantag

Slutförd

Nu när du har en god förståelse för spårnings- och hanteringsundanstag ska vi gå igenom att skapa undantag.

Du kanske redan känner till en situation som kan orsaka ett feltillstånd när du skriver kod. I dessa situationer är det användbart att skapa undantag som gör att annan kod kan inse vad problemet är.

Att skapa undantag kan också vara till hjälp vid beslutsfattande för annan kod. Som vi har sett tidigare kan kod, beroende på felet, fatta smarta beslut för att lösa, kringgå eller ignorera ett problem.

Astronauter begränsar sin vattenanvändning till cirka 11 liter per dag. Nu ska vi skapa en funktion som, beroende på antalet astronauter, kan beräkna hur mycket vatten som ska lämnas efter en dag eller mer:

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"

Prova med fem astronauter, 100 liter vatten kvar och två dagar kvar:

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

Det är inte särskilt användbart, eftersom ett underskott i liter bör vara ett fel. Sedan kan navigeringssystemet varna astronauterna om att det inte kommer att finnas tillräckligt med vatten kvar för alla om två dagar. Om du är en tekniker som programmerar navigeringssystemet kan du skapa ett undantag i water_left() funktionen för att avisera om feltillståndet:

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"

Kör den nu igen:

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!

I navigeringssystemet kan koden för att signalera aviseringen nu användas RuntimeError för att avisera:

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

Funktionen water_left() kan också uppdateras för att förhindra att typer som inte stöds skickas. Prova att skicka argument som inte är heltal för att kontrollera felutdata:

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'

Felet från TypeError är inte särskilt användarvänligt i samband med vad funktionen förväntar sig. Uppdatera funktionen så att den använder TypeError men med ett bättre meddelande:

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"

Försök nu igen för att få ett bättre fel:

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'