Felhantering i Python
Att hantera fel som uppstår under körningen av ett program är en viktig del i programmeringen. Fel kommer alltid uppstå, det kan vara alltifrån att utrymmet på hårddisken är slut, datorns minne är fullt, en fil som vi ska läsa inte finns eller har fel rättigheter. Vi ska aldrig anta att användaren matar in den typ av data som vi förväntar oss.
I denna artikel ska vi gå igenom hur man hanterar olika typer av fel på ett så korrekt sätt som möjligt i Python.
Python har inbyggda funktioner för felhantering, något som faktiskt inte alla
programspråk har. I Python är dessa try
och except
.
Ett exempelprogram
Första uppgiften är att ta reda på vilken typ av fel som kan uppstå, alltså vilken typ Traceback som genereras av olika fel. Detta gör vi genom att helt enkelt själva generera felen vi tror kommer att inträffa.
Vi gör ett mycket enkelt program som räknar ut kvadraten på ett tal. Användaren ska då mata in ett heltal och som svar ska programmet ge kvadraten på det tal som användaren skrev. Nedan visas programmet utan felhantering.
#!/usr/bin/env python3
while(True):
tal = int(input("Ange ett heltal: "))
svar = tal**2
print("Kvadraten på " + str(tal) + " är " + str(svar))
Ett mycket kort och enkelt program. Vi testkör programmet och matar in några heltal för att se så att allting fungerar som det ska. Programmet avslutas genom att trycka på CTRL-D eller CTRL-C.
Ange ett heltal: 5
Kvadraten på 5 är 25
Ange ett heltal: 2
Kvadraten på 2 är 4
Ange ett heltal: 9
Kvadraten på 9 är 81
Ange ett heltal: 7
Kvadraten på 7 är 49
Ange ett heltal: Traceback (most recent call last):
File "./felhantering1.py", line 4, in <module>
tal = int(input("Ange ett heltal: "))
EOFError
Programmet fungerar bra, men redan här ser vi första “felet”. När jag avslutar med CTRL-D får vi en Traceback, och det ser aldrig snyggt ut. Vi ska alltid hantera alla Tracebacks på ett snyggt sätt så att användaren aldrig får sådana här felmeddelanden. För användaren kan det här se ut som programmet blew up in his face.
Vår första uppgift här blir således att hantera CTRL-D. Då börjar vi med att
titta på vad just denna Traceback heter, vilket är EOFError, vilket stämmer
utmärkt. EOF står för End Of File. Eftersom vi tryckte på CTRL-D fick ju inte
input
-funktionen någon data och då blev det ett EOF.
Två saker vill jag ska hända när användaren trycker på CTRL-D. För det första
vill jag att användaren ska få en utskrift på skärmen i form av “Adjö, välkommen
åter” eller liknande. Den andra saken är att programmet ska avslutas på ett
korrekt sätt med noll som returvärde. Alla program i UNIX-liknande miljöer som
avslutas på ett korrekt sätt ska ge noll i returvärde. Ett program som
avslutas felaktigt kan ge vilket annat heltal som helst. För att åstadkomma
detta importerar vi sys
-modulen som har exit()
-funktionen. Vi bakar sedan in
raden tal = int(input("Ange ett heltal: "))
i ett try
-block. Den nya koden
ser nu ut som nedan.
#!/usr/bin/env python3
import sys
while(True):
try:
tal = int(input("Ange ett heltal: "))
except (EOFError):
print("\nAdjö, välkommen åter")
sys.exit(0)
svar = tal**2
print("Kvadraten på " + str(tal) + " är " + str(svar))
En testkörning av programmet visar att det hela fungerar utmärkt.
Ange ett heltal: 5
Kvadraten på 5 är 25
Ange ett heltal: 9
Kvadraten på 9 är 81
Ange ett heltal: Adjö, välkommen åter
Nu blev det en mycket snyggare avslutning på programmet. Dessutom returnerar
programmet noll, vilket vi kan kontrollera med echo $?
i UNIX-liknande
system. Det ger siffran 0 som svar.
Men om vi istället trycker på CTRL-C för att avsluta programmet, vad händer då? Vi testar:
Ange ett heltal: 6
Kvadraten på 6 är 36
Ange ett heltal: ^CTraceback (most recent call last):
File "./felhantering1.py", line 6, in <module>
tal = int(input("Ange ett heltal: "))
KeyboardInterrupt
Nu fick vi istället KeyboardInterrupt. Inte alls snyggt så vi tar och lägger
till denna Traceback till vår except()
. Programmet ser nu ut som nedan:
#!/usr/bin/env python3
import sys
while(True):
try:
tal = int(input("Ange ett heltal: "))
except (EOFError, KeyboardInterrupt):
print("\nAdjö, välkommen åter")
sys.exit(0)
svar = tal**2
print("Kvadraten på " + str(tal) + " är " + str(svar))
En testkörning visar att samma text och samma returvärde visas nu oavsett om vi avslutar med CTRL-C eller CTRL-D.
Ange ett heltal: 4
Kvadraten på 4 är 16
Ange ett heltal: ^C
Adjö, välkommen åter
Vad kan mer gå snett?
Nu när vi vet hur man lägger till exceptions är det dags att fråga oss vad som mer kan gå fel? Det vanligaste är att användaren skriver in något helt annat än ett heltal, t.ex. ett flyttal eller kanske rent av en text. Vi testar att göra så i vårt program och ser vad som händer.
Ange ett heltal: hej
Traceback (most recent call last):
File "./felhantering1.py", line 6, in <module>
tal = int(input("Ange ett heltal: "))
ValueError: invalid literal for int() with base 10: 'hej'
jake@red-dwarf:~/Kod$ ./felhantering1.py
Ange ett heltal: 3.14
Traceback (most recent call last):
File "./felhantering1.py", line 6, in <module>
tal = int(input("Ange ett heltal: "))
ValueError: invalid literal for int() with base 10: '3.14'
Här ser vi att fick samma sorts Traceback oavsett om vi skrev “hej” eller om vi
angav 3.14 som svar, nämligen ValueError. Men vi vill inte att programmet
avslutas bara för att vi skriver ett felaktigt värde. Programmet ska fortsättas
precis som vanligt, men användaren ska få en hjälpande text som förklarar att
han måste ange ett heltal. Vi skapar därför en ny except()
-rad i koden. Den
nya koden ser nu ut som nedan:
#!/usr/bin/env python3
import sys
while(True):
try:
tal = int(input("Ange ett heltal: "))
except (EOFError, KeyboardInterrupt):
print("\nAdjö, välkommen åter")
sys.exit(0)
except (ValueError):
print("Ange endast heltal!")
continue
svar = tal**2
print("Kvadraten på " + str(tal) + " är " + str(svar))
Det nya except()
-blocket fångar nu upp alla ValueError och hanterar dem
genom att först skriva ut texten “Ange endast heltal!”, därefter körs continue
som gör att den nuvarande iterationen i while()
-loopen hoppas över.
Vi testkör programmet och ser att allting nu faktiskt fungerar som tänkt. Programmet hanterar nu både felaktiga värden på ett snyggt sätt samt avslut av programmet med både CTRL-C och CTRL-D.
Ange ett heltal: 9
Kvadraten på 9 är 81
Ange ett heltal: hej
Ange endast heltal!
Ange ett heltal: 3.14
Ange endast heltal!
Ange ett heltal: ^C
Adjö, välkommen åter
Detta är en den generalla arbetsgången när man skapar felhantering för sin
program. Man testkör för att hitta felen, lägger in hantering av de fel man fick
fram, testkör igen, lägger in nya fel och testkör igen. Man håller på så tills
man hittat alla möjliga fel som kan inträffa. Det händer att man trots allt
missar olika typer av fel. Vad man kan göra då är att lägga in en generell
except()
som fångar upp alla övriga fel. Observera att man alltid bör undvika
generella except()
-block av den anledning att man inte på förväg vet vilka fel
som inträffar. Man döljer då eventuella buggar. Det är då bättre att användaren
kan rapportera den riktiga Tracebacken. Men här kan vi lägga in ett generellt
except()
-block efter att alla de kända felen har hanteras, mest som en
demonstration.
#!/usr/bin/env python3
import sys
while(True):
try:
tal = int(input("Ange ett heltal: "))
except (EOFError, KeyboardInterrupt):
print("\nAdjö, välkommen åter")
sys.exit(0)
except (ValueError):
print("Ange endast heltal!")
continue
except:
print("Något har blivit riktigt galet!")
sys.exit(1)
svar = tal**2
print("Kvadraten på " + str(tal) + " är " + str(svar))
Sådär, nu har vi lagt in en sista except()
som fångar upp alla övriga fel som
inte de två första except()
-blocken hanterar. Denna sista except()
avslutar
dessutom programmet genom att returnera en etta som returvärde, vilket i sin tur
signalerar till skalet att något oväntat har inträffat.
Relaterat
Senaste nyheterna och inläggen
CyberInfo Sverige är ett IT- och medieföretag i nordvästra Skåne som tillhandahåller böcker, utbildningar, nyheter och konsulttjänster inom Linux, BSD och programmering.
CyberInfo Sverige är godkänd för F-skatt, är momsregistrerat och innehar
utgivningsbevis för webbplatsen www.cyberinfo.se.