I Python används konstruktionen `try-except` för att elegant hantera undantag och förhindra programkrascher. Denna metod ökar robustheten hos din kod och minskar risken för oväntade avbrott. Denna artikel utforskar hur man hanterar undantag och identifierar vanliga scenarier där undantagshantering är ovärderlig. Som en extra bonus kommer vi även att visa hur man genererar egna undantag.
Vad är undantagshantering?
Undantag är allvarliga avvikelser eller fel som kan inträffa när ett program körs. Om dessa fel inte hanteras, kommer de att leda till att programmet avslutas abrupt. Undantagshantering är en teknik som används för att ta hand om dessa fel, så att de inte leder till att programmet krashar.
Låt oss illustrera vad ett undantag är med ett exempel:
user_input = input("Ange ett nummer: ") num = int(user_input) print("Ditt nummer dubbleras till:", num * 2)
Vid en första anblick ser koden ovan felfri ut. Den samlar in data från användaren och omvandlar den till ett heltal. Slutligen visas det dubbla av heltalet som användaren angav.
Om du kör programmet med ett heltal, som t.ex. 5, fungerar det som förväntat. Se nedan:
Låt oss nu anta att du kör programmet igen, men den här gången anger du texten ”hej” istället för ett nummer. Programmet kommer att krascha. Texten ”hej” kan inte konverteras till ett heltal, vilket resulterar i att ett undantag skapas, och därmed avslutas programmet.
Varför uppstår undantag och varför bör du hantera dem?
Undantag uppstår ofta när vi delar upp program i funktioner. Dessa funktioner anropas sedan för att utföra olika åtgärder.
I det tidigare exemplet anropade vi `input()`-funktionen för att samla in data, `int()`-funktionen för att konvertera texten till ett heltal och slutligen `print()`-funktionen för att visa resultatet.
Men när funktioner utför sina uppgifter kan de stöta på fel som de inte vet hur de ska hantera. I dessa situationer måste funktionerna stoppa exekveringen och signalera att ett fel har uppstått. De gör detta genom att generera ett undantag.
Den kod som har anropat funktionen har ansvaret att fånga upp dessa undantag och reagera på ett lämpligt sätt. Om detta inte görs kommer programmet att krascha vid ett fel, vilket vi såg i det tidigare exemplet.
Undantag är alltså en kommunikationsmekanism som ger en funktion möjlighet att signalera till anropande kod att ett fel har inträffat. Att reagera på dessa signaler på ett lämpligt sätt är själva kärnan i undantagshantering.
Olika typer av undantag
Det är viktigt att förstå att alla undantag inte är likadana. Det finns olika typer av undantag som representerar olika fel. Till exempel, om du försöker dividera ett tal med noll, uppstår ett `ZeroDivisionError`. Och ett `TypeError` uppstår när du försöker utföra en operation med fel datatyp. Här finns en fullständig lista över typer av undantag.
Hur man hanterar undantag
Som tidigare nämnts, är undantag nödsignaler som skickas ut av funktioner. Vår kod bör därför lyssna efter dessa signaler och reagera lämpligt när de aktiveras. För att hantera undantag använder vi i Python `try-except`-konstruktionen. Den grundläggande strukturen är:
try: # Kod som ska köras except: # Kod som körs om ett undantag genereras finally: # Kod som körs oavsett om ett undantag genereras eller ej
Konstruktionen består av tre nyckelord, som förklaras nedan:
`try`
Nyckelordet `try` markerar början på `try-except`-konstruktionen. Det indikerar ett kodblock som potentiellt kan generera ett undantag. Det är en instruktion till Python att försöka köra koden i detta block. Om ett undantag genereras, kommer programmet att stoppa exekveringen av `try`-blocket och istället gå vidare till `except`-blocket.
`except`
Nyckelordet `except` markerar kodblocket som kommer att exekveras om ett undantag genereras under körningen av `try`-blocket. Du kan definiera flera `except`-block för att hantera olika typer av undantag. Detta kommer att illustreras senare.
`finally`
Nyckelordet `finally` är det tredje och sista nyckelordet i `try-except`-konstruktionen. Det markerar ett kodblock som alltid kommer att köras, oavsett om ett undantag genererats eller inte.
Ett exempel
Här är ett exempel på hur du kan använda `try-except-finally` för att hantera undantag. Vi kommer att anpassa det tidigare exemplet:
try: user_input = input("Ange ett nummer: ") num = int(user_input) print("Ditt nummer dubbleras till:", num * 2) except: print("Något gick fel") finally: print("Denna kod kommer alltid att köras")
Om du kör koden ovan med ”5” som inmatning (en giltig inmatning) får du följande:
Om du kör den med ”hej” som inmatning får du följande:
När inget undantag genererades under körningen av `try`-blocket fortsatte programmet till `finally`-blocket. Men om ett undantag genererades under körningen av `try`-blocket gick programmet till `except`-blocket, och sedan till `finally`-blocket.
Du kan också anpassa hanteringen av specifika feltyper. Om du till exempel vill hantera `ValueError` och `KeyboardInterrupt`-undantag på olika sätt, kan du ändra koden så här:
try: user_input = input("Ange ett nummer: ") num = int(user_input) print("Ditt nummer dubbleras till:", num * 2) except ValueError: print("Värdet kan inte konverteras till ett heltal") except KeyboardInterrupt: print("Avbruten av tangentbordet") except: print("Fånga upp alla andra undantag") finally: print("Denna kod kommer alltid att köras")
I koden ovan har vi 3 `except`-block. Det första `except`-blocket fångar enbart `ValueError`-undantag, medan det andra endast fångar `KeyboardInterrupt`-undantag. Det sista `except`-blocket har ingen specifik undantagstyp att lyssna efter och fångar därför upp alla andra typer av undantag som inte fångas av de första två blocken.
När du kör koden ovan ska du få en utskrift som ser ut ungefär så här:
När ett undantag genereras kan du få tillgång till mer information om undantaget via undantagsobjektet. För att få tillgång till undantagsobjektet använder du nyckelordet `as`. Det används på följande sätt:
try: user_input = input("Ange ett nummer: ") num = int(user_input) print("Ditt nummer dubbleras till:", num * 2) except ValueError as e: print("Felaktigt värde:", e) except KeyboardInterrupt as e: print("Avbrott via tangentbordet:", e) except Exception as e: print("Något annat undantag inträffade:", e)
Hur man genererar undantag
Hittills har vi arbetat med undantag som har genererats av andra funktioner. Det är dock möjligt att generera undantag även i din egen kod. För att göra det använder du nyckelordet `raise`. Du måste också ange en klass som representerar vilken typ av undantag du vill generera, samt ett meddelande som är kopplat till undantaget.
I följande exempel använder vi den generiska `Exception`-klassen för att skapa ett generiskt undantag. Vi skickar sedan meddelandet till klassens konstruktor.
raise Exception('Något gick fel')
Om du kör koden ovan får du en utskrift som ser ut ungefär så här:
Du kan också ange olika typer av undantag. Till exempel kan du generera ett `TypeError`-undantag om ett värde har fel datatyp:
def double(x): if isinstance(x, int): return x * 2 else: raise TypeError('x borde vara ett heltal')
Eller om ett angivet värde ligger utanför ett acceptabelt intervall kan du generera ett `ValueError`:
def say_hello(name): if name == '': raise ValueError('Värdet ligger utanför acceptabelt område') else: print('Hej', name)
Du kan även skapa egna undantagstyper genom att ärva från `Exception`-klassen. Här är ett exempel:
class InvalidHTTPMethod(Exception): pass
I exemplet ovan har vi skapat en klass vid namn `InvalidHTTPMethod` som ärver från `Exception`-klassen. Vi kan använda den på samma sätt som tidigare för att generera undantag:
raise InvalidHTTPMethod('Måste vara GET eller POST')
Vanliga användningsområden för undantagshantering
Undantagshantering används i många olika sammanhang. Det tidigare exemplet visade hur det kan användas för att hantera felaktig användarinmatning. Det här avsnittet täcker ytterligare två situationer där undantagshantering är användbart. Det handlar om att hantera undantag som orsakas av nätverksförfrågningar och att hantera undantag när filer läses.
Göra nätverksförfrågningar
I exemplet nedan skickar vi en begäran till Google. Vi lyssnar efter undantag för att kunna hantera dem. Dessa undantag definieras i `requests.exceptions`-objektet.
import requests try: response = requests.get("https://google.com") # Kontrollera om statuskoden är mellan 200 och 299 (lyckad begäran) if 200 <= response.status_code < 300: print("Förfrågan lyckades!") else: print(f"Förfrågan misslyckades med statuskod: {response.status_code}") except requests.exceptions.RequestException as e: print(f"Ett RequestException-undantag inträffade: {e}") except requests.exceptions.ConnectionError as e: print(f"Ett ConnectionError-undantag inträffade: {e}") except requests.exceptions.Timeout as e: print(f"Ett Timeout-undantag inträffade: {e}") except requests.exceptions.TooManyRedirects as e: print(f"Ett TooManyRedirects-undantag inträffade: {e}") except requests.exceptions.HTTPError as e: print(f"Ett HTTPError-undantag inträffade: {e}") except Exception as e: print(f"Ett oväntat fel inträffade: {e}")
Läsa data från en fil
I det här sista exemplet läser vi data från en fil vid namn `hello.txt`. Vi hanterar även vanliga undantag som kan inträffa, som t.ex. `FileNotFoundError` och `IOError`.
try: with open(file_path, 'r') as file: data = file.read() print("Filens innehåll:") print(data) except FileNotFoundError as e: print(f"Ett FileNotFoundError-undantag inträffade: {e}") except IOError as e: print(f"Ett IOError-undantag inträffade: {e}") except Exception as e: print(f"Ett oväntat fel inträffade: {e}")
Slutsats
I den här artikeln undersökte vi vad undantag är och varför de genereras. Vi har också sett att vi behöver hantera dem för att göra koden mer robust och förhindra krascher. Slutligen gick vi igenom hur man hanterar undantag och även hur man genererar sina egna undantag.
Kolla även in vanliga Python-fel och hur du löser dem.