2.25. Fouten afhandelen

De meeste runtimeproblemen in Python verschijnen als uitzonderingen – een benoemde, gestructureerde manier om te melden dat er iets misging. ValueError, TypeError, KeyError, OSError, MemoryError zijn allemaal voorbeelden; elk is een klasse, en het opwerpen van zo’n uitzondering stopt de huidige aanroep en zoekt naar een afhandelaar in de omringende code.

2.25.1. try / except

Wikkel een codeblok in try om elke uitzondering die het opwerpt op te vangen:

try:
    n = int(input_text)
except ValueError:
    n = 0

Als de conversie int(...) mislukt, springt de besturing naar het except-blok in plaats van de fout verder te verspreiden. Als input_text een geldige gehele-getallenstring was, wordt het except-blok overgeslagen.

Een enkele try kan meerdere except-blokken hebben, elk dat een ander soort fout opvangt:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python vergelijkt in volgorde; de eerste waarvan de uitzonderingsklasse past, handelt het probleem af. Het opvangen van Exception (de basisklasse voor bijna alles) handelt elke fout af; reserveer dat voor de buitenste laag van een programma waar het alternatief een crash is.

Waarschuwing

Een kale except: (geen klasse na het sleutelwoord) vangt ook KeyboardInterrupt op – de uitzondering die de IDE stuurt wanneer je op de stop-knop drukt om een draaiend script te onderbreken. Een lus die in een kale except: pass is gewikkeld, slikt de onderbreking in en blijft draaien, waardoor er geen manier is om het script te stoppen behalve een stroomcyclus.

Geef de voorkeur aan except Exception: boven een kale except: wanneer je werkelijk breed moet opvangen. KeyboardInterrupt erft van BaseException, niet van Exception, dus bij except Exception: blijft de stop-knop werken.

2.25.1.1. De uitzondering inspecteren

Om het bericht te lezen dat aan een uitzondering is gekoppeld, benoem je deze met as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

De variabele die met as is gebonden, is alleen geldig binnen het except-blok.

2.25.2. else en finally

Een try-blok heeft twee optionele extra’s.

else wordt alleen uitgevoerd wanneer de try voltooide zonder een uitzondering op te werpen:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

Door “wat te doen wanneer het werkte” in else te plaatsen, blijft het try-blok smal – alleen de regel die kan mislukken hoort in try.

finally wordt aan het einde uitgevoerd wat er ook gebeurt – of de try nu slaagde, opwierp en werd afgehandeld, of opwierp en op het punt staat zich te verspreiden:

try:
    do_work()
finally:
    cleanup()
Stroomdiagram dat de vier mogelijke paden door try / except / else / finally toont: succes gaat naar else dan finally; een opgevangen uitzondering gaat naar except dan finally; een niet-opgevangen uitzondering gaat naar finally dan verspreidt zich.

finally wordt altijd uitgevoerd. else wordt alleen uitgevoerd op het niet-uitzonderingspad.

Geef voor de meeste verkrijg/vrijgeef-patronen de voorkeur aan een contextmanager boven een try / finally-paar – de bron beheert zelf zijn eigen opruiming.

2.25.3. Veelvoorkomende ingebouwde uitzonderingen

Een korte lijst van de uitzonderingen die je vaak zult tegenkomen:

  • ValueError – juist type, verkeerde waarde (bytes([300]) – 300 is het juiste type, maar valt buiten het geldige bytebereik van 0..255).

  • TypeError – helemaal het verkeerde type (len(42)).

  • KeyError – ontbrekende sleutel in een dict.

  • IndexError – index voorbij het einde van een reeks.

  • AttributeError – toegang tot een attribuut dat niet bestaat ("abc".foo).

  • OSError – een fout in het bestandssysteem of de I/O.

  • MemoryError – de heap is op. Op een geheugenbeperkte runtime kan dit tijdens normaal gebruik gebeuren – niet alleen in pathologische gevallen.