Exception handeling

T. Issaris

13 september 2021

Fout afhandeling

Defensief programmeren: “verdedigen” tegen mogelijk foutieve invoer

Zonder foutafhandeling

def bmi(gewicht, lengte):
    return gewicht / lengte**2

b = bmi(77, 1.87)
print(f"BMI: {b}")

Wat bij ongeldige parameter?

def bmi(gewicht, lengte):
    return gewicht / lengte**2

b = bmi(77, 0) # ?
Traceback (most recent call last):
  File "prog2/eh001.py", line 4, in <module>
    b = bmi(77, 0) # ?
  File "prog2/eh001.py", line 2, in bmi
    return gewicht / lengte**2
ZeroDivisionError: integer division or modulo by zero

Exceptie

Python raises/gooit een exceptie:

Traceback (most recent call last):
...
ZeroDivisionError: integer division or modulo by zero

De ZeroDivisionError exceptie geeft aan dat er geprobeerd werd om door 0 te delen.

Hoe lees je een traceback

De “traceback” kan helpen om uit te zoeken waar het probleem zich voordeed:

Traceback (most recent call last):
  File "prog2/eh001.py", line 4, in <module>
    b = bmi(77, 0) # ?
  File "prog2/eh001.py", line 2, in bmi
    return gewicht / lengte**2

Op regel 4 in bestand eh001.py werd “bmi” opgeroepen en dit zorgde voor uitvoering van regel 2, die de deling door 0 veroorzaakte.

Foutafhandeling

Om aan te geven dat een operatie niet uitgevoerd kan worden, wordt dikwijls gebruik gemaakt van een “speciale” waarde.

Gebruik speciale waarde -1

def bmi(gewicht, lengte):
    if lengte == 0:
        return -1
    return gewicht / lengte**2

b = bmi(80, 1.835)
if b == -1:
    print("error! lengte mag niet 0 zijn")
else:
    print("BMI:", b)

Probleem

  • Wat is de waarde -1 ook een geldig antwoord is?
  • Wat als ieder geheel getal een geldig antwoord kan zijn?
  • Welke speciale waarde kunnen we dan gebruiken?

Special return value: None

def bmi(gewicht, lengte):
    if lengte == 0:
        return None
    return gewicht / lengte**2

b = bmi(77, 1.87)
if b is None:
    print("error! lengte mag niet 0 zijn")
else:
    print(f"BMI: {b}")

Probleem lokale afhandeling

Nadelen:

  • De afhandeling van de fout dient nu direct na de oproep te gebeuren.
  • Veel extra code die oproeper moet doen

Lokale afhandeling

def gezondheid():
    b = bmi(77, 1.87)
    if b is None:
        print("error! lengte mag niet 0 zijn")
    else:
        print(f"BMI: {b}")
    c = ...
    d = ...
    return b, c, d

b, c, d = gezondheid()
# ?

Hoe moeten we de fout nu afhandelen?

Oplossing: exeptions

De code die het probleem afhandelt dient vlakbij de oproeper te staan. Excepties laten een meer flexibele manier van werken toe.

Raising exceptions

Aangeven van het probleem doe je met “raise”:

def bmi(gewicht, lengte):
    if lengte == 0:
        raise RuntimeError("lengte fout")
    return gewicht / lengte**2

b = bmi(0, 1.87)
print(f"BMI: {b}")

Exception handling

Het afhandelen van de exceptie doe je met “try/except”:

def bmi(gewicht, lengte):
    if lengte == 0:
        raise RuntimeError("lengte fout")
    return gewicht / lengte**2

try:
    b = bmi(0, 1.87)
    print(f"BMI: {b}")
except RuntimeError:
    print("probleem met berekenen BMI")

Lokale afhandeling

De functie gezondheid kan nu eenvoudiger gehouden worden, zonder fout afhandeling:

def gezondheid():
    b = bmi(77, 1.87)
    c = ...
    d = ...
    return b, c, d

try:
    b, c, d = gezondheid()
    print(f"BMI: {b}")
except RuntimeError:
    print("error! lengte mag niet 0 zijn")

Exception classes

We gebruikten in voorgaande slides de RuntimeError class om een probleem aan te geven. Er bestaan nog vele andere voorgedefinieerde excepties:

  • ValueError
  • TypeError
  • NotImplementedError

ValueError

Voorbeeld foutieve conversie string naar integer:

v = int("10")
v = int("hello")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'hello'

TypeError

Voorbeeld foutieve concatenatie str met integer:

print("ik woon op nummer " + 108)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

Eigen exceptions

Buiten de voorgedefinieerde excepties, kan je ook zelf excepties maken.

Custom exceptions

class LengteError(RuntimeError):
    pass

def bmi(gewicht, lengte):
    if lengte == 0:
        raise LengteError
    return gewicht / lengte ** 2

try:
    b = bmi(0, 1.87)
    print(f"BMI: {b}")
except LengteError:
    print("probleem met lengte")

Eigen exceptions

Eigen excepties laten toe om verschillende fouten op verschillende manieren af te handelen.

class GewichtError(RuntimeError):
    pass
class LengteError(RuntimeError):
    pass

def bmi(gewicht, lengte):
    if gewicht <= 0:
        raise GewichtError
    if lengte == 0:
        raise LengteError
    return gewicht / lengte ** 2

try:
    b = bmi(0, 1.87)
    print(f"BMI: {b}")
except GewichtError:
    print("probleem met gewicht")
except LengteError:
    print("probleem met lengte")
// reveal.js plugins