Skip to content

6. De uitzonderingen

We gaan nu verder met de uitzonderingen.

  

Programma (exceptions_01)

Het eerste script illustreert de noodzaak om uitzonderingen af te handelen.


# -*- coding=utf-8 -*-

# er wordt een fout veroorzaakt
x=4/0

We veroorzaken opzettelijk een fout om te zien welke informatie de interpreter genereert. De uitvoer op het scherm is als volgt:

1
2
3
4
Traceback (most recent call last):
  File "D:\data\istia-1112\python\tutoriel\exceptions_01.py", line 4, in <module>
    x=4/0
ZeroDivisionError: integer division or modulo by zero

Regel 4 geeft ons:

  • het type van de uitzondering: ZeroDivisionError;
  • de bijbehorende foutmelding: integer division or modulo by zero. Deze is in het Engels. Dit is iets wat men wellicht wil wijzigen.

De belangrijkste regel bij het programmeren is dat je er alles aan moet doen om „onverwachte“ crashes zoals hierboven te voorkomen. Zelfs als er een fout optreedt, moet het programma op een nette manier worden afgesloten en informatie geven over de fout die zich heeft voorgedaan.

De syntaxis voor uitzonderingsafhandeling is als volgt:


try:
    actions susceptibles de lancer une exception
except [classe d'exception, ...]:
    actions de gestion de l'exception
finally:
   actions toujours exécutées qu'il y ait exception ou non

In de try-blok wordt de uitvoering van de acties gestopt zodra er een uitzondering optreedt. In dat geval wordt de uitvoering voortgezet met de acties in de except-clausule.

De clausule


except [MyException, ...]:

vangt uitzonderingen van het type MyException of daarvan afgeleide types op. Wanneer er in een try-blok een uitzondering optreedt, doorloopt de interpreter de bij het try-blok behorende except-clausules in de volgorde waarin ze zijn geschreven. Hij stopt bij de eerste except-clausule waarmee de opgetreden uitzondering kan worden afgehandeld. Als er geen wordt gevonden, wordt de uitzondering doorgegeven aan de aanroepende methode. Als deze een try/except-blok bevat, wordt de uitzondering opnieuw afgehandeld; anders wordt de uitzondering verder doorgegeven in de keten van aangeroepen methoden. Uiteindelijk komt de uitzondering terecht bij de Python-interpreter. Deze stopt dan het uitgevoerde programma en geeft een foutmelding weer zoals in het vorige voorbeeld. De regel is dus dat het hoofdprogramma alle uitzonderingen moet opvangen die vanuit de aangeroepen methoden kunnen worden doorgegeven.

Een uitzondering bevat informatie over de fout die zich heeft voorgedaan. Deze informatie kan met de volgende syntaxis worden opgehaald:


except MyException as informations:

informatie is een tuple die de informatie met betrekking tot de uitzondering bevat.

De syntaxis


except MyException, erreur:

wijst de foutmelding die bij de uitzondering hoort toe aan de variabele erreur.

Om een uitzondering te genereren, gebruikt men de syntaxis


raise MyException(param1, param2, ...)

waarbij MyException meestal een klasse is die is afgeleid van de klasse Exception. De parameters die aan de constructor van de klasse worden doorgegeven, zijn beschikbaar in de except-clausule van de structuren voor het opvangen van uitzonderingen.

Deze concepten worden geïllustreerd door het volgende script.


Programma (exceptions_02)

Het volgende script behandelt fouten expliciet:


# -*- coding=utf-8 -*-

i=0
# er wordt een fout veroorzaakt en deze wordt afgehandeld
x=2
try:
    x=4/0
except ZeroDivisionError, erreur:
    print ("%s : %s ") % (i, erreur)
# de waarde van x is niet gewijzigd
print "x=%s" % (x)

# we beginnen opnieuw
i+=1
try:
    x=4/0
except Exception, erreur:
    # de meest algemene uitzondering wordt opgevangen
    print ("%s : %s ") % (i, erreur)

# er kunnen meerdere soorten uitzonderingen worden opgevangen 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # deze uitzondering doet zich hier niet voor
    print ("%s : %s ") % (i, erreur)
except Exception, erreur:
    # we vangen de meest algemene uitzondering op
    print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    # er wordt een specifiek type onderschept
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)

# we beginnen opnieuw en veranderen de volgorde 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # deze uitzondering treedt hier niet op
    print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    # we vangen een specifiek type af
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
    # de meest algemene uitzondering wordt opgevangen
    print ("%s : (Exception) %s ") % (i, erreur)

# een except-clausule zonder argumenten
i+=1
try:
    x=4/0
except:
    # er wordt geen aandacht besteed aan de aard van de uitzondering of aan het foutbericht
    print ("%s : il y a eu un probleme ") % (i)

# een ander type uitzondering
i+=1
try:
    x=int("x")
except ValueError, erreur:
    print ("%s : %s ") % (i, erreur)

# een uitzondering bevat informatie in een tuple die toegankelijk is voor het programma
i+=1
try:
    x=int("x")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# er kunnen uitzonderingen worden gegenereerd
i+=1
try:
    raise ValueError("param1","param2","param3")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# men kan eigen uitzonderingen maken
class MyError(Exception):
    pass

# de uitzondering MyError wordt gegenereerd
i+=1
try:
    raise MyError("info1","info2", "info3")
except MyError as infos:
    print ("%s : %s ") % (i, infos)

# de uitzondering MyError wordt gegooid
i+=1
try:
    raise MyError("mon msg d'erreur")
except MyError, erreur:
    print ("%s : %s ") % (i, erreur)

# je kunt elk type object genereren
class Objet:
    def __init__(self,msg):
        self.msg=msg
    def __str__(self):
        return self.msg

i+=1
try:
    raise Objet("pb...")
except Objet as erreur:
    print "%s : %s" % (i, erreur)

# de finally-clausule wordt altijd uitgevoerd
# ongeacht of er een uitzondering optreedt of niet
i+=1
x=None
try:
    x=1
except:
    print "%s : exception" % (i)
finally:
    print "%s : finally x=%s" % (i,x)

i+=1
x=None
try:
    x=2/0
except:
    print "%s : exception" % (i)
finally:
    print "%s : finally x=%s" % (i,x)

Opmerkingen:

  • regels 6-9: er wordt omgegaan met een deling door nul;
  • regel 8: de exacte uitzondering die optreedt, wordt opgevangen;
  • regel 11: vanwege de opgetreden uitzondering heeft x geen waarde gekregen en is de waarde dus niet gewijzigd;
  • regels 15-19: we doen hetzelfde nogmaals, maar vangen nu een uitzondering van een hoger niveau van het type Exception op. Aangezien de uitzondering ZeroDivisionError is afgeleid van de klasse Exception, zal de clausule except deze stoppen;
  • regels 23-33: er worden meerdere except-clausules geplaatst om verschillende soorten uitzonderingen af te vangen. Er wordt slechts één except-clausule uitgevoerd, of geen enkele, als de uitzondering aan geen enkele except-clausule voldoet;
  • regels 51-55: de clausule except hoeft geen argumenten te hebben. In dat geval stopt deze alle uitzonderingen;
  • regels 59-62: introduceren de uitzondering ValueError;
  • regels 66-69: de door de uitzondering meegevoerde informatie wordt opgehaald;
  • regels 73-76: hier wordt uitgelegd hoe een uitzondering kan worden gegenereerd;
  • regels 79-84: illustreren het gebruik van een eigen uitzonderingsklasse MyError;
  • regels 79-80: de klasse MyError is slechts afgeleid van de basisklasse Exception. Ze voegt niets toe aan haar basisklasse. Maar nu kan ze expliciet worden genoemd in de except-clausules;
  • regels 97-107: laten zien dat je in Python in feite elk type object kunt gebruiken en niet alleen objecten die zijn afgeleid van de klasse Exception;
  • regels 109-127: illustreren het gebruik van de clausule finally.

De schermresultaten zijn als volgt:

0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None

Programma (exceptions_03)

Dit nieuwe script illustreert het doorgeven van uitzonderingen in de keten van aanroepende methoden:


# -*- coding=utf-8 -*-

# een eigen uitzondering
class MyError(Exception):
    pass

# drie methoden
def f1(x):
    # uitzonderingen worden niet afgehandeld – ze worden automatisch doorgegeven
    return f2(x)

def f2(y):
    # uitzonderingen worden niet afgehandeld – ze worden automatisch doorgegeven
    return f3(y)

def f3(z):
    if (z % 2) == 0:
        # als z even is, wordt er een uitzondering gegenereerd 
        raise MyError("exception dans f3")
    else:
        return 2*z

#---------- main

# uitzonderingen worden doorgegeven via de keten van aangeroepen methoden
# totdat een methode deze onderschept. Hier is dat main
try:
    print f1(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

# een methode kan de uitzonderingen die ze doorgeeft aanvullen
def f4(x):
    try:
        return f5(x)
    except MyError as infos:
        # de uitzondering wordt aangevuld en vervolgens opnieuw afgegeven
        raise MyError(infos,"exception dans f4")

def f5(y):
    try:
        return f6(y)
    except MyError as infos:
        # we vullen de uitzondering aan en sturen deze vervolgens opnieuw door
        raise MyError(infos, "exception dans f5")

def f6(z):
    if (z % 2) == 0:
        # er wordt een uitzondering gegenereerd
        raise MyError("exception dans f6")
    else:
        return 2*z

#---------- main
try:
    print f4(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

Opmerkingen:

  • regels 27-30, in de aanroep main --> f1 --> f2 --> f3 (regel 28), zal de uitzondering MyError, die door f3 wordt gegenereerd, worden doorgegeven tot aan main. Deze wordt vervolgens afgehandeld door de clausule except op regel 29;
  • regels 55-58: bij de aanroep main --> f4 --> f5 --> f6 (regel 56) zal de door f6 gegenereerde uitzondering MyError terugkeren naar main. Deze wordt vervolgens afgehandeld door de clausule except op regel 29. Deze keer wordt de uitzondering MyError tijdens het doorlopen van de keten van aanroepende methoden aangevuld met informatie die door elke doorlopen methode wordt toegevoegd.

De schermresultaten zijn als volgt:

type :<class '__main__.MyError'>, arguments : exception dans f3
type :<class '__main__.MyError'>, arguments : (MyError(MyError('exception dans f6',), 'exception dans f5'), 'exception dans f4')