Python Try-Except: Håndter unntak elegant & unngå krasj!

Forståelse av Python Try Except: En komplett guide

I Python er try-except-strukturen et kraftig verktøy for å håndtere feil på en elegant måte uten å krasje programmet. Feilhåndtering øker påliteligheten til koden din og reduserer risikoen for uventede feil. Denne artikkelen gir en detaljert forklaring av hvordan du håndterer unntak, samt vanlige bruksområder der feilhåndtering er avgjørende. I tillegg skal vi se på hvordan du selv kan utløse unntak.

Hva er unntakshåndtering?

Unntak er kritiske feil som oppstår under kjøring av et program. Hvis disse feilene ikke håndteres, vil de forårsake at programmet stopper. Derfor er unntakshåndtering en mekanisme for å fange og behandle disse feilene, slik at programmet kan fortsette å kjøre uten avbrudd.

La oss se på et enkelt eksempel for å illustrere hva et unntak er:

    user_input = input("Skriv inn et tall: ")
    num = int(user_input)
    print("Dobbelt så mye er:", num * 2)
  

Ved første øyekast ser denne koden ganske uskyldig ut. Den mottar input fra brukeren og konverterer det til et heltall. Deretter skriver den ut det doble av tallet.

Hvis du kjører dette programmet med inndataverdien 5, vil det fungere som forventet:

Men la oss anta at du kjørte programmet igjen, og denne gangen skrev du inn teksten «hei» i stedet for et tall. Dette vil føre til at programmet krasjer, fordi teksten «hei» ikke kan konverteres til et heltall. Et unntak oppstår, og programmet stopper.

Hvorfor oppstår unntak og hvorfor bør vi håndtere dem?

Når vi utvikler programmer, bryter vi dem ofte ned i mindre funksjoner. Disse funksjonene blir kalt for å utføre spesifikke oppgaver.

I eksemplet ovenfor kalte vi input-funksjonen for å motta brukerens input, deretter int-funksjonen for å konvertere teksten til et heltall, og til slutt print-funksjonen for å vise resultatet.

Når en funksjon utfører oppgaven sin, kan den støte på feil som den ikke vet hvordan den skal håndtere. I slike tilfeller må funksjonen avbryte sin operasjon og signalisere at en feil har oppstått. Dette gjøres ved å utløse et unntak.

Det er da opp til koden som kalte funksjonen å fange opp dette unntaket og respondere på riktig måte. Hvis dette ikke gjøres, vil programmet krasje så snart det støter på en feil, slik vi så i det forrige eksemplet.

Unntak er altså en kommunikasjonsmekanisme som lar en funksjon som er kalt sende et signal om en feilsituasjon tilbake til koden som kalte den. Måten denne koden responderer på, kalles unntakshåndtering.

Ulike typer unntak

Det er viktig å vite at det finnes ulike typer unntak, som representerer forskjellige typer feil. For eksempel, hvis du prøver å dele et tall med null, vil en ZeroDivisionError oppstå. Hvis du forsøker en operasjon med en ugyldig datatype, vil en TypeError oppstå. Du kan finne en komplett liste over unntakstyper i Python-dokumentasjonen.

Hvordan håndtere unntak

Som nevnt tidligere, er unntak feilsignaler som sendes fra funksjoner. Koden vår bør lytte etter disse signalene og reagere hensiktsmessig når de mottas. For å håndtere unntak på riktig måte, bruker vi try-except-strukturer i Python. Den grunnleggende strukturen ser slik ut:

    try:
        # Kode som kan generere et unntak
    except:
        # Kode som kjøres hvis et unntak oppstår
    finally:
        # Kode som kjøres uansett om et unntak oppstod eller ikke
    

Denne strukturen består av tre nøkkelord som er forklart nedenfor:

try

try-nøkkelordet markerer begynnelsen på try-except-strukturen. Det definerer en kodeblokk som potensielt kan utløse et unntak. Det forteller Python-tolken at den skal prøve å kjøre koden i denne blokken. Hvis et unntak oppstår, vil programmet umiddelbart hoppe over resten av koden i try-blokken og gå til except-blokken.

except

except-nøkkelordet definerer kodeblokken som vil bli utført dersom et unntak oppstår mens koden i try-blokken kjøres. Du kan definere flere except-blokker for å håndtere ulike typer unntak på forskjellige måter. Dette skal vi illustrere senere.

finally

finally-nøkkelordet markerer en kodeblokk som vil bli utført uansett om et unntak oppstod eller ikke. Dette er nyttig for kode som må kjøres, for eksempel å lukke filer eller frigjøre ressurser.

Et eksempel

Her er et eksempel på hvordan de ovennevnte nøkkelordene kan brukes til å håndtere et unntak. Vi skal modifisere det forrige eksemplet:

        try:
          user_input = input("Skriv inn et tall: ")
          num = int(user_input)
          print("Dobbelt så mye er:", num * 2)
        except:
          print("Noe gikk galt")
        finally:
          print("Denne koden vil alltid kjøre")
      

Hvis du kjører denne koden med 5 som inndata, vil du få følgende output:

Hvis du derimot kjører den med «hei» som inndata, får du dette:

Når ingen unntak ble utløst i try-blokken, fortsatte programmet til finally-blokken. Men når et unntak ble utløst, hoppet programmet til except-blokken og deretter finally-blokken.

Du kan også håndtere spesifikke typer feil. Hvis du for eksempel vil håndtere ValueError– og KeyboardInterrupt-unntak på forskjellige måter, kan du gjøre dette:

      try:
        user_input = input("Skriv inn et tall: ")
        num = int(user_input)
        print("Dobbelt så mye er:", num * 2)
      except ValueError:
        print("Verdien kan ikke konverteres til et tall")
      except KeyboardInterrupt:
        print("Mottok et avbrudd fra tastaturet")
      except:
        print("En annen feil oppstod")
      finally:
        print("Denne koden vil alltid kjøre")
    

Her har vi tre except-blokker. Den første fanger kun ValueError-unntak, den andre KeyboardInterrupt-unntak, og den siste fanger alle andre typer unntak som ikke ble fanget av de to første.

Når du kjører denne koden, vil du få output som ligner på dette:

Når et unntak oppstår, kan du få tilgang til mer informasjon om unntaket ved å bruke as-nøkkelordet. Det brukes slik:

    try:
        user_input = input("Skriv inn et tall: ")
        num = int(user_input)
        print("Dobbelt så mye er:", num * 2)
    except ValueError as e:
        print("Feil verdi:", e)
    except KeyboardInterrupt as e:
        print("Tastaturavbrudd:", e)
    except Exception as e:
        print("En annen feil oppstod:", e)
  

Hvordan utløse unntak

Hittil har vi fokusert på unntak som er utløst av andre funksjoner. Du kan også utløse dine egne unntak i koden din. Dette gjøres med raise-nøkkelordet. Du spesifiserer en unntaksklasse og en tilhørende beskrivelse.

Vi bruker Exception-klassen for å lage et generisk unntak i følgende eksempel. Beskrivelsen sendes til klassens konstruktør.

        raise Exception('Noe gikk galt')
    

Hvis du kjører denne kodebiten, vil du se noe slikt:

Du kan også utløse spesifikke typer unntak. For eksempel kan du utløse et TypeError-unntak hvis en verdi har feil datatype:

        def double(x):
          if isinstance(x, int):
            return x * 2
          else:
              raise TypeError('x må være et tall')
    

Eller hvis en verdi er utenfor de akseptable grensene, kan du utløse en ValueError:

      def si_hei(navn):
        if navn == '':
          raise ValueError('Verdi utenfor grenser')
        else:
            print('Hei', navn)
      

Du kan også lage dine egne unntaksklasser ved å arve fra Exception-klassen:

        class UgyldigHTTPMetode(Exception):
          pass
    

Her har vi laget en klasse UgyldigHTTPMetode som arver fra Exception-klassen. Vi kan bruke den til å utløse unntak på samme måte:

        raise UgyldigHTTPMetode('Må være GET eller POST')
    

Vanlige bruksområder for unntakshåndtering

Unntakshåndtering brukes i mange forskjellige scenarier. Det forrige eksemplet viste hvordan du kan håndtere feil som skyldes brukerinput. Nå skal vi se på to situasjoner til der unntakshåndtering er nyttig: håndtering av feil i nettverksforespørsler og ved lesing av filer.

Lage nettverksforespørsler

I eksemplet nedenfor sender vi en forespørsel til Google. Vi lytter etter unntak for å håndtere dem. Disse unntakene er definert i requests.exceptions-objektet.

        import requests

        try:
            response = requests.get("https://google.com")

            if 200 <= response.status_code < 300:
                print("Forespørselen var vellykket!")
            else:
                print(f"Forespørselen feilet med statuskode: {response.status_code}")
        except requests.exceptions.RequestException as e:
            print(f"RequestException oppstod: {e}")
        except requests.exceptions.ConnectionError as e:
            print(f"ConnectionError oppstod: {e}")
        except requests.exceptions.Timeout as e:
            print(f"Timeout oppstod: {e}")
        except requests.exceptions.TooManyRedirects as e:
            print(f"TooManyRedirects oppstod: {e}")
        except requests.exceptions.HTTPError as e:
            print(f"HTTPError oppstod: {e}")
        except Exception as e:
            print(f"En uventet feil oppstod: {e}")
    

Lese data fra en fil

I dette siste eksemplet leser vi data fra filen «hello.txt». Vi håndterer vanlige unntak som FileNotFoundError og IOError.

        try:
          with open(file_path, 'r') as file:
              data = file.read()
              print("Filinnhold:")
              print(data)
        except FileNotFoundError as e:
            print(f"FileNotFoundError oppstod: {e}")
        except IOError as e:
            print(f"IOError oppstod: {e}")
        except Exception as e:
            print(f"En uventet feil oppstod: {e}")
      

Konklusjon

Denne artikkelen har forklart hva unntak er, hvorfor de oppstår, og hvorfor vi må håndtere dem for å gjøre koden mer robust og unngå krasj. Vi har også gått gjennom hvordan du håndterer unntak ved hjelp av try-except-finally strukturen, og hvordan du selv kan utløse unntak.

Du kan nå fortsette å lære om vanlige Python-feil og hvordan du løser dem.