Håndtere krasj
Krasj av programmet en bra ting, fordi vi ønsker å vite at noe er feil så fort som mulig. Men i noen tilfeller ønsker vi at programmet skal håndtere krasjen selv; dette gjelder egentlig bare når vi vet på forhånd hva slag krasj som kan oppstå, og er ikke et lurt triks å bruke dette for å skyve problemer under teppet.
Generelt vil jeg anbefale å være sparsom med bruken av try og except; hvis det kan håndteres uten på en enkel og grei måte, er det som oftest å foretrekke. Kode som er basert på mye try og except i kontrollflyten er litt mer utfordrende å feilsøke. Samtidig, dersom å bruke try/except sparer mye omstendelig kode kan det være å foretrekke likevel.
Krasjhåndtering med try/except
# Håndtere en krasj
# -- Prøv å gi programmet noe som ikke er et tall
# -- Prøv å gi programmet et tall som er for stort
# Se: programmet krasjer ikke selv om brukeren gir dårlig input svar!
animals = ["katt", "hund", "kanin", "hamster", "krokodille"]
user_input = input(f"Velg ett tall [0-{len(animals) - 1}]:")
try:
i = int(user_input)
animal = animals[i]
except:
# Kjøres dersom try-blokken krasjet
print("Ugyldig valg!")
else:
# Kjøres dersom try-blokken gikk bra
print("Gratulerer, du fikk en ny", animal)
print("Nå er programmet ferdig")
Bruk krasjhåndtering (try/except) med varsomhet. Å bruke mye krasjhåndetring kan gjøre koden din litt vanskeligere å feilsøke.
# FARE!! bruk av except: håndterer for mange krasjer --> vanskelig å feilsøke!
# -- Prøv nå å gi programmet en GYLDIG tall som input
# Se: programmet krasjer ikke, men gjør i stedet en logisk feil! FYFYFY!
animals = ["katt", "hund", "kanin", "hamster", "krokodille"]
user_input = input(f"Velg ett tall [0-{len(animals) - 1}]:")
try:
i = int(user_input)
animal = animal[i] # Skrivefeil (s mangler)! Vi VIL krasje her med NameError!
except:
print("Ugyldig valg!") # Oops! Kommer hit selv om input er gyldig
else:
print("Gratulerer, du fikk en ny", animal)
print("Nå er programmet ferdig")
Man bør alltid spesifisere hvilken type krasj man håndterer.
# Spesifiser hvilken type krasj du håndterer
#
# -- Prøv å gi programmet en GYLDIG tall som input (se: krasjer)
# -- Fiks kodefeilen (skrivefeilen) ved å rette koden der det krasjer
# -- Prøv nå å gi programmet et tall som er for stort
# -- Prøv nå å gi programmet noe som ikke er et tall
animals = ["katt", "hund", "kanin", "hamster", "krokodille"]
user_input = input(f"Velg ett tall [0-{len(animals) - 1}]:")
try:
i = int(user_input)
animal = animal[i] # Skrivefeil (s mangler) -- men nå krasjer vi! YAY!
except ValueError:
print("Ugyldig valg, du må oppgi et tall!")
except IndexError:
print("Tallet du oppgav er ugyldig!")
else:
print("Gratulerer, du fikk en ny", animal)
print("Nå er programmet ferdig")
# NameError blir ikke fanget av except nå,
# så vi oppdager det nå hvis variabeler har feil navn.
Stilguide for krasjhåndtering
- Bruk krasjhåndtering for å håndtere andre sine feil; ikke for å skjule egne feil i koden.
- Eksempler på andre sine feil: en bruker skriver inn ugyldig input, filen du prøver å lese fra finnes ikke eller har feil innhold, nettverket er nede, etc.
- Eksempler på egne feil: du har skrevet feil variabelnavn, du har gitt feil argumenter til en funksjon, du ufører en beregning på gal måte etc.
- Hvis du enkelt kan løse problemet uten try/except, er det ofte en bedre løsning.
- For eksempel: benytt if-setninger for å håndtere hjørnetilfeller som er enkle å sjekke.
- Ha minst mulig kode i try-blokken.
- Flytt så mye kode som mulig utenfor try-blokken: enten før try-blokken begynner eller inn i else-blokken.
- Alltid angi hvilken type feil du håndterer.
Krasje på egen hånd
Kodeordet raise
brukes for å krasje på egen hånd. Dette kan brukes for å krasje med en feilmelding som gir mer detaljert informasjon enn ellers, eller for å krasje programmet så tidlig som mulig dersom noe er galt.
Å krasje med
raise
gir noenlunde samme funksjonalitet som å skriveassert False
(se kursnotater om feil og debugging). Forskjellen ligger primært i at du kan lage flere ulike typer feil medraise
. En krasj forårsaket avassert False
vil alltid krasje med typenAssertionError
.
# Krasj programmet dersom input ikke er gyldig
food = input()
if food not in ["salat", "tomat", "agurk", "paprika"]:
raise ValueError(f"Maten '{food}' er ikke akseptabel.")
print("Takk for maten!")
# Krasj programmet dersom argumenter ikke er gyldige
def process_payment(amount, card_number):
if amount <= 0:
raise ValueError("Cannot process payment for amount <= 0")
if len(card_number) != 16:
raise ValueError("Card number must be 16 digits long")
... # do the actual payment processing here
print("Payment processed successfully")
process_payment(-40, "1234567890123456")
Kan brukes for å gi ekstra informasjon ved krasj
def foo(i, j):
y = j
for x in range(i, j):
try:
y += abs(y/x)
except ZeroDivisionError as err: # err variabel som 'husker' krasjen
# Skriver ut debug-informasjon
print("Divisjon med 0")
print("Lokale variabler: ", locals())
raise err # Kaster samme krasj på nytt
return x
print(foo(-5, 10))