Cursus
Wanneer Python-code tijdens runtime op problemen stuit, wordt er vaak een exceptie opgegooid. Als je die niet afhandelt, crasht je programma. Met try-except-blokken kun je ze echter opvangen, netjes herstellen en je applicatie laten doorlopen.
Deze tutorial gaat niet over de basis van excepties; dat behandelen we al in onze gids Exception & Error Handling in Python. In plaats daarvan duiken we hier diep in op try-except in de praktijk: hoe je je blokken structureert, veelgemaakte fouten vermijdt en best practices toepast die je code betrouwbaarder maken in realistische scenario’s.
Aan het eind begrijp je niet alleen hoe try-except werkt, maar ook hoe je het op de Pythonic manier gebruikt; foutafhandelingscode schrijven die duidelijk, onderhoudbaar en production-ready is.
Als je nog midden in je Python-leertraject zit, raad ik je aan om de skill track Python Programming Fundamentals te bekijken, die je helpt om alle essentiële skills op te bouwen.
Waarom focussen op try-except?
Foutafhandeling in Python draait in de kern om het ontwerpen van programma’s die met het onverwachte kunnen omgaan. En hoewel er veel hulpmiddelen zijn om fouten af te handelen, is try-except de ruggengraat van Python’s aanpak.
Waarom is het zo belangrijk?
- Het weerspiegelt de filosofie van Python. In Python is de gangbare stijl EAFP; Easier to Ask Forgiveness than Permission. In plaats van alles vooraf te checken (bestaat dit bestand? is deze input geldig? is de server beschikbaar?), probeer je gewoon de operatie. Als die faalt, vang je de exceptie en handel je die af. Dat leidt tot kortere, schonere code.
- Het houdt code draaiend in de echte wereld. Gebruikersinput is rommelig, bestanden verdwijnen, API’s falen en netwerken vallen uit. Met
try-exceptkun je code schrijven die niet instort bij het eerste probleem, maar juist slim reageert. - Het is flexibel voor beginners én professionals. Een beginner gebruikt
try-exceptmisschien om eenValueErrorop te vangen bij het converteren van input naar een integer. Een productiesysteem kan het gebruiken om fouten te loggen, mislukte operaties te herhalen of custom excepties op te gooien die debuggen makkelijker maken.
Daarom zoomt dit artikel in op try-except. Het beheersen ervan maakt het verschil tussen scripts die alleen werken onder perfecte omstandigheden en software die robuust, onderhoudbaar en klaar voor productie is.
Anatomie van een try‑except-blok
In zijn simpelste vorm:
try:
risky_thing()
except SomeError:
handle_it()
Wanneer Python een fout tegenkomt binnen het try-blok, springt het naar het bijbehorende except-blok in plaats van het programma te laten crashen.
Je kunt dit patroon ook uitbreiden met else- en finally-clausules:
try:
do_something()
except ValueError:
print("Bad value!")
else:
print("All good.")
finally:
clean_up()
Hier is een basisvoorbeeld dat werkt:
try:
x = int(input("Enter a number: "))
except ValueError:
print("That wasn’t a number.")
else:
print("You entered", x)
finally:
print("Done.")
De except handelt de fout af, else draait alleen als alles goed ging, en finally draait altijd, zelfs als je Ctrl‑C indrukt of afsluit.
Je kunt de basis van gebruikersinvoer in Python onder de knie krijgen, van simpele prompts tot geavanceerde validatie- en foutafhandelingstechnieken, met onze tutorial Python User Input: Handling, Validation, and Best Practices .
Meerdere excepties afhandelen
Soms is één except niet genoeg. Verschillende codepaden kunnen op verschillende manieren falen, en alles onder de zon vangen met een kale except: helpt niet echt; het kan bugs verbergen en debuggen tot een hoofdpijndossier maken.
Stel, je probeert iets naar een integer te converteren en het daarna te delen. Dit is wat je niet moet doen:
try:
result = int(user_input) / 2
except:
print("Something went wrong.")
Dit vangt alles, inclusief dingen die je waarschijnlijk niet wilde verbergen, zoals typefouten in variabelnamen of onverwachte excepties die op echte problemen wijzen.
Wees in plaats daarvan specifiek:
try:
result = int(user_input) / 2
except ValueError:
print("That wasn't a number.")
except ZeroDivisionError:
print("Division by zero?")
Als je meerdere exceptietypen hebt die op dezelfde manier moeten worden behandeld, kun je ze groeperen zoals dit:
try:
result = some_function()
except (TypeError, ValueError):
print("Something was wrong with the data.")
Zo blijf je duidelijk over wat er mis kan gaan, zonder steeds hetzelfde blok te dupliceren.
En dan is er except Exception, wat beter is dan een kale except, maar nog steeds wat te breed. Richt je liever op de specifieke fouten die je verwacht.
Else- en finally-blokken gebruiken
Wanneer de code in het try-blok slaagt en er geen excepties worden opgegooid, voert Python het else-blok uit.
try:
user_id = get_user_id()
except LookupError:
print("User not found.")
else:
print("Welcome, user", user_id)
Dit helpt om je foutafhandelingslogica te scheiden van het happy path, wat het leesbaarder maakt.
Dan is er finally. Dat maakt niet uit wat er gebeurde; het draait sowieso. Exceptie? Draait nog steeds. Geen exceptie? Draait nog steeds. Programma crasht met Ctrl‑C? Draait nog steeds. Ideaal voor opruimwerk:
try:
f = open("data.txt")
process(f)
except IOError:
print("Couldn’t open file.")
finally:
f.close()
Onthoud alleen: als het bestand nooit is geopend, bestaat f misschien niet eens, dus wees voorzichtig. Je kunt in plaats daarvan de contextmanager van Python gebruiken (with open(...) as f:) als je met bestanden werkt. Dat is veiliger.
Als je belangrijke technieken wilt leren, zoals exceptieafhandeling en foutpreventie, om de KeyError -exceptie in Python effectief af te handelen, raad ik onze tutorial Python KeyError Exceptions and How to Fix Them aan.
Python try-except best practices en veelvoorkomende valkuilen
Eerlijk is eerlijk: try‑except-blokken schrijven is een beetje een kunst. Goed gedaan voorkomen ze dat je programma crasht. Slecht gedaan begraven ze bugs zó diep dat je ze pas merkt als je app omvalt en gebruikers klagen.
Hier zijn gewoontes die het waard zijn om op te bouwen:
Houd try-blokken klein. Wikkel geen honderd regels code in één try. Dat maakt het alleen maar lastiger om te weten wat het probleem veroorzaakte. Wikkel in plaats daarvan alleen de code die kan falen:
# Good
try:
value = int(data)
except ValueError:
print("Couldn’t convert.")
# Bad
try:
# Tons of unrelated logic
value = int(data)
do_more()
something_else()
except ValueError:
print("Huh?")
Vermijd het checken van voorwaarden vóór je de actie uitvoert als het makkelijker is om het gewoon te proberen en de fout op te vangen. Python heeft hier een naam voor: EAFP, Easier to Ask Forgiveness than Permission. Als je controleert of een bestand bestaat en het dan opent, maak je een raceconditie. In plaats daarvan:
try:
with open("file.txt") as f:
content = f.read()
except FileNotFoundError:
print("No file.")
Dit patroon voorkomt een veelvoorkomend probleem waarbij het bestand tussen de check en de open-aanroep kan verdwijnen. Probeer het gewoon. Als het faalt, vang de fout.
Vermijd ook het stilhouden van fouten door alles te vangen en niets te doen. Doe dit niet:
try:
something()
except:
pass
Tenzij je echt weet wat je doet, is dit dé plek waar bugs zich verschuilen en vermeerderen. Als je het móet negeren, wees dan specifiek:
try:
something()
except TimeoutError:
# okay to ignore in this case
pass
En log de fouten ergens. Ze compleet doorslikken betekent dat jij in de toekomst geen idee hebt wat er misging.
Ingebouwde vs. custom excepties
Python wordt geleverd met flink wat ingebouwde excepties, en die dekken verrassend veel gevallen. Je bent er vast al een paar tegengekomen:
ValueError: wanneer iets het juiste type heeft maar een ongeldige waarde, zoalsint("hello").TypeError: wanneer je een operatie probeert op het verkeerde type, zoals een string en een getal optellen.ZeroDivisionError: je raadt het al, delen door nul.FileNotFoundError: proberen een bestand te openen dat er niet is.KeyError: wanneer een key ontbreekt in een dictionary.
Deze zijn nuttig, en in de meeste scripts of apps zijn ze meer dan genoeg. Maar soms wil je iets opgooien dat beschrijvender is, iets dat past in de wereld van jouw project, niet alleen die van Python.
Stel, je schrijft een app die online bestellingen verwerkt, en je wilt een fout opgooien wanneer een gebruiker iets probeert te kopen dat niet op voorraad is. Je zou ValueError kunnen gebruiken, maar dat is vrij generiek. Het vertelt de volgende lezer van je code niet wat er gebeurde.
Hier schitteren custom excepties.
class OutOfStockError(Exception):
pass
def check_inventory(product_id):
if not in_stock(product_id):
raise OutOfStockError(f"Product {product_id} is out of stock.")
Door de custom exceptie te maken, voeg je een betekenislaag toe. Je geeft jezelf ook meer controle; je kunt precies deze specifieke situatie opvangen:
try:
check_inventory("shirt-001")
except OutOfStockError:
print("Sorry, that item is sold out.")
Het is een klein detail, maar het maakt je code beter te begrijpen en te onderhouden, zeker in grotere projecten. En het werkt goed samen met logging en monitoring; custom exceptienamen zijn veel makkelijker terug te vinden dan een vage ValueError.
Logging, opnieuw opgooien en exception chaining
Er is zo’n moment tijdens het debuggen: je ziet een traceback, staart ernaar en realiseert je dat je geen idee hebt waarom iets faalde. Daar komt logging om de hoek kijken. Het gaat niet alleen om het vastleggen van fouten, maar om toekomstige jij (of je team) de kruimelsporen te geven om te achterhalen wat er misging.
Stel dat je een exceptie opvangt en die wilt loggen voordat je verdergaat:
import logging
logging.basicConfig(level=logging.ERROR)
try:
do_something()
except ValueError as e:
logging.error("Failed to do something: %s", e)
raise
Die raise aan het eind is belangrijk: het gooit dezelfde exceptie opnieuw op na het loggen. Zonder dat heb je de fout gewoon doorgeslikt. Soms is dat prima, maar meestal niet.
Dan is er de raise from-truc. Die gebruik je als je de ene fout afhandelt maar een andere moet opgooien, en je de oorspronkelijke niet wilt kwijtraken. Python laat je ze koppelen:
try:
connect_to_database()
except TimeoutError as e:
raise ConnectionError("Database unavailable.") from e
Zo vertelt de traceback het hele verhaal. Je krijgt de nieuwe ConnectionError, maar je ziet ook de TimeoutError die ’m veroorzaakte.
Je kunt de oorspronkelijke fout ook onderdrukken (wat je meestal niet zou moeten doen) zo:
raise ConnectionError("Just this error, nothing else.") from None
Maar tenzij je een goede reden hebt, helpt het behouden van de volledige keten iedereen te begrijpen wat er misging en hoe het is opgestapeld.
Je kunt de basis van logging in Python leren in onze Logging in Python Tutorial.
Voorbeelden en use-cases uit de praktijk
Over exceptieafhandeling praten is één ding, maar het valt op z’n plek als je het in echte code ziet.
Neem gebruikersinvoer. Vraag het aan iedereen die ooit een command-line tool of formulier-validator bouwde: gebruikers voeren de vreemdste dingen in. Vraag je om een getal? Iemand typt “twelve”. Of plakt een telefoonnummer. Of drukt gewoon op Enter. Het gebeurt.
In plaats van een lange lijst met “wat als”-checks te schrijven, kun je dit doen:
while True:
user_input = input("Enter a number: ")
try:
number = int(user_input)
break
except ValueError:
print("Try again with a whole number.")
Deze code raakt niet in paniek bij onzininput. Hij vraagt de gebruiker om het opnieuw te proberen en loopt door totdat het goed is. Veel schoner dan if-statements stapelen voor elke randgeval.
Nog een: lezen uit een bestand dat misschien niet bestaat.
try:
with open("config.json") as f:
settings = f.read()
except FileNotFoundError:
print("Missing config file. Using defaults.")
settings = "{}"
Je hoeft niet te checken of het bestand er is. Probeer het gewoon te openen, en als dat faalt, ga je verder. Als je vooraf had gecheckt (os.path.exists()), had iemand anders het bestand misschien tussen de check en het openen verwijderd. Dat is een raceconditie, niet iets wat je wilt debuggen.
Netwerkaanvragen zijn een andere goudmijn voor excepties. Je kunt niet altijd op het internet vertrouwen. Servers gaan down. Verbindingen vallen weg. DNS faalt. Dus als je zoiets doet:
import requests
try:
response = requests.get("https://example.com/data")
response.raise_for_status()
except requests.exceptions.RequestException as e:
print("Network problem:", e)
Die RequestException-basisklasse vangt handig vrijwel alles wat requests kan opgooien, van time-outs tot slechte responses. Je hoeft geen tien verschillende except-blokken te schrijven tenzij je ze anders wilt afhandelen.
En als je automationscripts of backendservices schrijft, kan het omhullen van kernlogica met try‑except-blokken het verschil maken tussen één falende taak en een compleet uitvallend systeem. Je wilt fouten gelogd, herstelbare taken opnieuw geprobeerd en niet-herstelbare netjes afgesloten, niet met cryptische stack traces die eindeloos door je logs rollen.
Leer over Python-automatisering, inclusief fundamentele concepten, kernbibliotheken, werken met data, AI-versterkingen en best practices in onze tutorial Python Automation: A Complete Guide.
Try-except in Python: geavanceerde toepassingen
Tegenwoordig heb je vast gezien hoe flexibel try‑except kan zijn. Maar flexibiliteit heeft twee kanten. Het is makkelijk om van behulpzaam naar slordig te gaan zonder dat je het doorhebt. Zo houd je het onder controle.
Vang specifieke excepties. Als je weet wat er mis kan gaan, noem het bij naam:
try:
result = int(data)
except ValueError:
# Only catches invalid numbers, not everything else under the sun
Vermijd kale except:. Doe dit niet tenzij je iets heel specifieks afhandelt. Het vangt dingen als KeyboardInterrupt, SystemExit en andere zaken die je waarschijnlijk niet wilt stilhouden.
Gebruik else en finally als ze de code duidelijker maken. Forceer ze niet alleen omdat ze bestaan. Als het normale pad van je code wordt begraven in een try, verplaats het dan misschien naar een else.
Houd je try-blokken klein. Hoe meer je opneemt, hoe lastiger het wordt om te achterhalen welke regel de fout veroorzaakte. Wikkel alleen het deel dat kan falen.
Log fouten waar nodig, zeker in productie. Zelfs als je niet crasht, maakt weten wat (en wanneer) faalde het debuggen later veel makkelijker.
Custom excepties zijn niet verplicht, maar ze helpen. Als je app-specifieke problemen hebt, definieer je eigen fouten. Ze kunnen logs leesbaarder maken en je code zelfverklarender.
Nog één ding: gebruik geen excepties voor flow control tenzij het niet anders kan. Het is verleidelijk om logica te schrijven als “probeer dit; als het faalt, doe dat”, maar als het iets is dat je voortdurend verwacht, is er waarschijnlijk een schonere manier.
Conclusie
Zoals ik in dit artikel heb uitgelegd, gaat exceptieafhandeling niet alleen om het vermijden van crashes. Het gaat om code die problemen verwacht, ze zonder drama afhandelt en doorgaat, of afsluit op een manier die logisch is. Het is het verschil tussen een gebruiker die een behulpzame melding krijgt en iemand die wordt teruggezet naar de terminal met een lange, onleesbare traceback.
Ik raad je van harte aan om meer te leren over excepties, met hands-on oefeningen, in ons hoofdstuk Catching Exceptions in Python van onze OOP in Python-cursus.
Python try-except FAQ's
Wat is het verschil tussen een error en een exceptie in Python?
Een error verwijst meestal naar een probleem waardoor Python je code niet eens kan starten, zoals een ontbrekende dubbele punt of een typefout in een keyword. Dit zijn syntaxfouten. Een exceptie treedt op terwijl de code draait, zoals proberen te delen door nul of een bestand openen dat niet bestaat. Excepties kun je opvangen en afhandelen zodat je programma niet crasht.
Is het slecht om een kale except in Python te gebruiken?
Ja, in de meeste gevallen wel. Een kale except vangt alles, inclusief dingen die je niet wilde vangen, zoals keyboard interrupts of system-exitsignalen. Dat maakt debuggen lastig. Het is beter om specifieke excepties te vangen, zoals ValueError of FileNotFoundError, zodat je precies weet wat je afhandelt.
Wanneer moet ik else gebruiken in een try-except-blok?
Gebruik else wanneer je code wilt uitvoeren alleen als er geen exceptie optrad in het try-blok. Het helpt je succespadlogica gescheiden te houden van de foutafhandeling, wat je code leesbaarder en onderhoudbaarder kan maken.
Wat is het nut van finally als ik al een except-blok heb?
finally draait hoe dan ook, of er nu een fout is of niet. Het is perfect voor opruimen: bestanden sluiten, resources vrijgeven, transacties terugdraaien, enzovoort. Zelfs als er een fout optreedt of je vroegtijdig afsluit, draait finally nog steeds.
Moet ik altijd `try-except` gebruiken in plaats van eerst voorwaarden te checken?
Niet altijd, maar vaak is het beter om het gewoon te try en de fout te catchen als het mislukt. Python-ontwikkelaars noemen dit EAFP, “Easier to Ask Forgiveness than Permission.” Het is sneller en voorkomt bepaalde bugs, vooral wanneer er iets kan veranderen tussen de check en de actie (zoals een bestand dat wordt verwijderd).

