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.
Kommentarer
Kommentarsfältet är modererat. Det innebär att alla kommentarer granskas av ansvarig utgivare före publicering.
Du väljer själv om du vill ange ditt riktiga namn, en pseudonym eller vara helt anonym. Ingen registrering behövs.
Relaterade artiklar
-
Hämta data från API:er med cURL och jq
Med cURL och jq går det att extrahera data från API:er direkt från kommandoraden. Jq är en JSON-tolkare och beskrivs av utvecklarna som sed och awk för JSON.
-
Pythonmiljö i Docker
Ett vanligt användningsområde för Docker är att skapa och köra virtuella Pythonmiljöer. Med en Dockerfile och Docker Compose kan vi automatisera hela processen, från skapandet av miljön till exekveringen.
-
Python och trigonometri
Lite uppfräschning av trigonometri och Python är aldrig fel. Här får vi lära oss hur man kan rita upp rätvinkliga trianglar – direkt i Python – om vi känner till två av sidorna. För detta kommer vi att använda modulerna turtle och math.
-
Python i Windows utan installation
Det går att använda Python i Windows, även utan att installera det. Detta är användbart om du har en dator där du inte har rättigheter att installera program. Det kan till exempel vara en skoldator eller arbetsdator.
-
Ett primtalsprogram i C
Här ska vi få en kort introduktion till programspråket C genom att göra ett litet enkelt primtalsprogram. Program består utav tre filer. Två styck källkodsfiler och en styck header-fil. I header-filen lägger vi något som kallas för funktionsprototyper. En funktionsprototyp använder man för att tala om för programmet hur en funktion ser ut, det vill säga vilka argument funktionen tar samt vilken datatyp den returernar. Detta gör gör man för att slippa lägga funktionen överst i samma fil som
main()
. Header-filen döper vi till skrivprm.h.
Senaste nyheterna och inläggen
-
Hämta data från API:er med cURL och jq
Med cURL och jq går det att extrahera data från API:er direkt från kommandoraden. Jq är en JSON-tolkare och beskrivs av utvecklarna som sed och awk för JSON.
-
Pythonmiljö i Docker
Ett vanligt användningsområde för Docker är att skapa och köra virtuella Pythonmiljöer. Med en Dockerfile och Docker Compose kan vi automatisera hela processen, från skapandet av miljön till exekveringen.
-
Retroloppis i Påarp
Idag var det retroloppis hos Andreas Nilsson i Påarp. På baksidan av huset fanns hundratals spel uppradade på långa bord. Trots friska vindar och sval temperatur var loppisen välbesökt.
-
Ny bok om Ansible
Till hösten släpper CyberInfo Sverige sin nya bok – Ansible från grunden. Boken riktar sig till alla som vill lära sig grunderna i Ansible. Den börjar med att sätta upp SSH-nycklar och köra enkla ad hoc-kommandon mot en värd. Därefter går vi vidare med att bygga egna playbooks och roller.
-
Det nya Docker Compose
Sedan i mitten av förra året har det gamla Python-baserade
docker-compose
-kommandot sakta ersatts av det nyare Go-baseradedocker compose
. Det nya kommandot är ett plugin till Docker istället för ett fristående kommando som tidigare.
Utvalda artiklar
-
Retroloppis i Påarp
Idag var det retroloppis hos Andreas Nilsson i Påarp. På baksidan av huset fanns hundratals spel uppradade på långa bord. Trots friska vindar och sval temperatur var loppisen välbesökt.
-
Stort deltagande på årets Gubbdata
I helgen var det Gubbdata i Lund – ett av Sveriges största demoparty. På plats fanns cirka ett hundra deltagare, alla med en passion för retrodatorer.
-
Kapad identitet
I mitten av september förra året blev Kristina utsatt för ett id-kapningsförsök. Någon hade ansökt om ett lån på 250 000 kr i hennes namn. Låneansökan gick ut till ett 20-tal banker.
-
Amiga-scenen lever än
I en villa mellan Ljungskile och Stenungsund den 27 november träffades de igen, ett gäng glada Amiga-entusiaster. Man träffas för att prata, titta på demos, spela och ha allmänt trevligt.
-
Fällande dom för mannen med polismejlen
Den man som under nästan två års tid uppmärksammade polisen på att han fick polisrelaterad mejl skickad till sig dömdes idag för dataintrång till dagsböter. Domen avser den inloggning till Frontex, Europeiska gräns- och kustbevakningsbyrån, som mannen testade.
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, säkerhet och programmering.
CyberInfo Sverige är godkänd för F-skatt, är momsregistrerat och innehar
utgivningsbevis för webbplatsen www.cyberinfo.se.