Kraschsäkring
Ditt program möter verkligheten
Du har byggt en meny som snurrar tills användaren väljer att avsluta. Den fungerar perfekt, när användaren gör exakt rätt. Men vad händer när användare är användare? I inmatningsrutan står redan ordet “Katt”, ett fullt rimligt input när vi frågar efter 1-3.
Redo.
Programmet exploderar. int() fick en sträng den inte kan tolka som ett tal och kastar ValueError: invalid literal for int() with base 10: 'Katt'. Testa inmatningen 2 och kör igen, då fungerar allt. Det är hela poängen: koden är korrekt för giltiga inmatningar och värdelös för alla andra.
I ett riktigt program hade det här varit slutet. Loopen avbryts, allt användaren hunnit göra sedan start är borta. Ett program som kraschar vid ogiltig inmatning är inte ett färdigt program, det är ett program som ännu inte mött verkligheten.
Verkliga användare gör det här hela tiden. Inte av illvilja, utan för att de läser fort, för att de inte förstår vad som förväntas, för att de testar gränserna utan att tänka på det. En robust applikation överlever det. Den korrigerar, frågar om och fortsätter, och den ger aldrig tillbaka en röd vägg av feltext till en användare som inte kan läsa den.
Verktyget heter try/except En kontrollstruktur för felhantering. Koden i try-blocket körs normalt. Om ett undantag uppstår hoppar Python omedelbart till except-blocket i stället för att krascha. . Det låter dig omsluta kod som kan krascha och bestämma vad som ska hända om den gör det, utan att hela programmet dör.
Undantag, när Python ger upp
När Python stöter på ett fel det inte kan hantera kastar det ett Undantag (exception) Ett objekt som Python skapar och kastar när ett fel uppstår under körning. Om undantaget inte fångas propagerar det uppåt i anropsstacken och kraschar programmet. : ett objekt som beskriver felet och avbryter det normala programflödet. Fångas undantaget inte upp når det till slut toppen av programmet, och då kraschar körningen.
Du känner redan igen de vanligaste, och namnet är det du ska fånga:
- ValueError Uppstår när ett värde har rätt typ men felaktigt innehåll, till exempel när int() tar emot en sträng som inte kan tolkas som ett heltal. , rätt typ men fel innehåll: `int("Katt")`
TypeError, inkompatibla typer:"hej" + 5NameError, ett namn Python inte känner igen:print(variabel_som_inte_finns)
try/except
try/except omsluter koden som kan krascha. Lyckas try-blocket hoppas except över helt. Misslyckas det körs except-blocket i stället för att programmet dör:
Redo.
Samma inmatning som nyss, men nu överlever programmet. Byt till 2 och kör igen: då hoppas except-blocket över och du ser bara resultatet.
Problemet med bara try/except
Grundformen löser halva problemet. Den förhindrar kraschen, men den löser inte inmatningen:
Redo.
Kör koden. Undantaget fångas, meddelandet skrivs ut, och sedan kraschar programmet ändå, den här gången med NameError: name 'val' is not defined. Tilldelningen hann aldrig ske, så variabeln finns inte. Vi bytte ut en krasch mot en annan.
Att flytta if-satsen in i try-blocket löser det inte heller, det gömmer bara problemet. Det vi faktiskt behöver är att fråga igen tills vi får ett giltigt svar. Vi behöver en loop, och den har du redan.
Loopa tills svaret duger
Kombinationen som löser problemet på riktigt är while True: runt try/except, med ett break som bara nås när inläsningen lyckades:
Redo.
Inmatningsrutan innehåller två rader: först “Katt”, sedan “5”. Kör koden och följ förloppet. Första varvet misslyckas, felet fångas, break nås aldrig, och loopen går ett varv till. Andra varvet lyckas, break körs och programmet fortsätter. Lägg till fler ogiltiga rader före femman och se att loopen aldrig ger upp.
Placeringen av break är hela poängen. Den ligger sist i try-blocket, alltså på raden efter den som kan krascha. Går inläsningen fel hoppar Python till except och break hoppas över, och det är precis därför loopen frågar om.
Fånga fel typ, och fånga ingenting
Det här är en subtil fallgrop: fångar du fel undantagstyp har du inte fångat något alls.
Redo.
Kör koden. Den kraschar, trots att där står ett try/except. int("katt") kastar ValueError, och except TypeError: bryr sig inte om ValueError. Byt TypeError mot ValueError och kör igen, då fungerar hanteringen.
Felmeddelandet i terminalen är alltid ditt bästa besked om vilken typ du behöver fånga. Läs namnet på undantaget, och fånga exakt det.
Uppgift: Kraschsäkra menyn
Öppna meny.py från Den eviga loopen.
Kör programmet och skriv en bokstav när det frågar efter ett menyval. Läs felmeddelandet och notera vilken undantagstyp som kastas.
Lägg
try/exceptrunt inläsningen så att programmet skriver ett tydligt meddelande i stället för att krascha. Kör igen med samma felaktiga inmatning.Programmet lever, men beter sig fortfarande konstigt efter ett felaktigt svar. Bygg om inläsningen till Valideraren:
while True:runttry/except, medbreaksist itry-blocket.Testa noggrant: skriv text när en siffra förväntas, tryck Enter utan att skriva något, skriv ett decimaltal. Menyn ska överleva samtliga och fråga om.
Gå till Source Control-panelen i VS Code, stagea filen, skriv commit-meddelandet
Kraschsäkring: try/except och Validerarenoch pusha.
Vad händer om
Du ändrar except ValueError: till bara except:, kör programmet i terminalen och trycker Ctrl+C mitt i en inmatningsprompt. Kan du avbryta programmet? Förklara vad som händer och varför en alltför generös fångare är ett problem.
Motivera & reflektera
Motivera varför du fångar en specifik undantagstyp i stället för allt. Vad kan gå fel den dag programmet innehåller en bugg du inte känner till?