Introduction au langage Python par l'exemple


précédentsommairesuivant

VII. Les exceptions

Nous nous intéressons maintenant aux exceptions.

Image non disponible

Le premier script illustre la nécessité de gérer les exceptions.

Programme (exceptions_01)
Sélectionnez
1.
2.
3.
4.
# -*- coding=utf-8 -*-

# on provoque une erreur
x=4/0

On provoque volontairement une erreur pour voir les informations produites par l'interpréteur. Le résultat écran est le suivant :

 
Sélectionnez
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

La ligne 4 nous donne :

  • le type de l'exception : ZeroDivisionError ;
  • le message d'erreur associé : integer division or modulo by zero. Il est en anglais. C'est quelque chose qu'on peut vouloir changer.

La règle essentielle en programmation est qu'on doit tout faire pour éviter les plantages " sauvages " comme celui ci-dessus. Même en cas d'erreur, le programme doit se terminer proprement en donnant des informations sur l'erreur qui s'est produite.

La syntaxe d'une gestion d'exceptions est la suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
try:
    actions susceptibles de lancer une exception
except [classe d'exceptions, ...]:
    actions de gestion de l'exception
finally:
   actions toujours exécutées qu'il y ait exception ou non

Dans le try, l'exécution des actions s'arrête dès qu'une exception survient. Dans ce cas, l'exécution se poursuit avec les actions de la clause except.

La clause

except [MyException…]:

intercepte les exceptions de type MyException ou dérivé. Lorsque dans un try se produit une exception, l'interpréteur examine les clauses except associées au try dans l'ordre où elles ont été écrites. Il s'arrête sur la première clause except permettant de traiter l'exception qui s'est produite. S'il n'en trouve aucune, l'exception remonte à la méthode appelante. Si celle-ci a un try / except, l'exception est de nouveau gérée sinon elle continue à remonter la chaîne des méthodes appelées. En dernier ressort, elle arrive à l'interpréteur Python. Celui-ci arrête alors le programme exécuté et affiche un message d'erreur du type montré dans l'exemple précédent. La règle est donc que le programme principal doit arrêter toutes les exceptions qui peuvent remonter des méthodes appelées.

Une exception transporte avec elle des informations sur l'erreur qui s'est produite. On peut les obtenir avec la syntaxe suivante :

except MyException as informations:

informations est un tuple qui transporte les informations liées à l'exception.

La syntaxe

except MyException, erreur:

affecte à la variable erreur, le message d'erreur lié à l'exception.

Pour lancer une exception, on utilise la syntaxe

raise MyException(param1, param2…)

où le plus souvent MyException est une classe dérivée de la classe Exception. Les paramètres passés au constructeur de la classe seront disponibles à la clause except des structures d'interception des exceptions.

Ces concepts sont illustrés par le script suivant qui gère explicitement les erreurs :

Programme (exceptions_02)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
# -*- coding=utf-8 -*-

i=0
# on provoque une erreur et on la gère
x=2
try:
    x=4/0
except ZeroDivisionError, erreur:
    print ("%s : %s ") % (i, erreur)
# la valeur de x n'a pas changé
print "x=%s" % (x)

# on recommence
i+=1
try:
    x=4/0
except Exception, erreur:
    # on intercepte l'exception la plus générale
    print ("%s : %s ") % (i, erreur)

# on peut intercepter plusieurs types d'exceptions 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # cette exception ne se produit pas ici
    print ("%s : %s ") % (i, erreur)
except Exception, erreur:
    # on intercepte l'exception la plus générale
    print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    # on intercepte un type précis
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)

# on recommence en changeant l'ordre 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # cette exception ne se produit pas ici
    print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    # on intercepte un type précis
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
    # on intercepte l'exception la plus générale
    print ("%s : (Exception) %s ") % (i, erreur)

# une clause except sans argument
i+=1
try:
    x=4/0
except:
    # on ne s'intéresse pas à la nature de l'exception ni au msg d'erreur
    print ("%s : il y a eu un probleme ") % (i)

# un autre type d'exception
i+=1
try:
    x=int("x")
except ValueError, erreur:
    print ("%s : %s ") % (i, erreur)

# une exception transporte des informations dans un tuple accessible au programme
i+=1
try:
    x=int("x")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# on peut lancer des exceptions
i+=1
try:
    raise ValueError("param1","param2","param3")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# on peut créer ses propres exceptions
class MyError(Exception):
    pass

# on lance l'exception MyError
i+=1
try:
    raise MyError("info1","info2", "info3")
except MyError as infos:
    print ("%s : %s ") % (i, infos)

# on lance l'exception MyError
i+=1
try:
    raise MyError("mon msg d'erreur")
except MyError, erreur:
    print ("%s : %s ") % (i, erreur)

# on peut lancer n'importe quel type d'objet
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)

# la clause finally est toujours exécutée
# qu'il y ait exception ou non
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)

Notes :

  • lignes 6-9 : on gère une division par zéro ;
  • ligne 8 : on intercepte l'exception exacte qui se produit ;
  • ligne 11 : à cause de l'exception qui s'est produite, x n'a pas reçu de valeur et n'a donc pas changé de valeur ;
  • lignes 15-19 : on refait la même chose mais en interceptant une exception de plus haut niveau de type Exception. Comme l'exception ZeroDivisionError dérive de la classe Exception, la clause except l'arrêtera ;
  • lignes 23-33 : on met plusieurs clauses except pour gérer plusieurs types d'exception. Une seule clause except sera exécutée ou aucune si l'exception ne vérifie aucune clause except ;
  • lignes 51-55 : la clause except peut n'avoir aucun argument. Dans ce cas, elle arrête toutes les exceptions ;
  • lignes 59-62 : introduisent l'exception ValueError ;
  • lignes 66-69 : on récupère les informations transportées par l'exception ;
  • lignes 73-76 : introduisent la façon de lancer une exception ;
  • lignes 79-84 : illustrent l'utilisation d'une classe d'exception propriétaire MyError ;
  • lignes 79-80 : la classe MyError se contente de dériver la classe de base Exception. Elle n'ajoute rien à sa classe de base. Mais maintenant, elle peut être nommée explicitement dans les clauses except ;
  • lignes 97-107 : illustrent qu'en Python on peut en fait lancer n'importe quel type d'objet et pas simplement des objets dérivés de la classe Exception ;
  • lignes 109-127 : illustrent l'utilisation de la clause finally.

Les résultats écran sont les suivants :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
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

Ce nouveau script illustre la remontée des exceptions dans la chaîne des méthodes appelantes :

Programme (exceptions_03)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
# -*- coding=utf-8 -*-

# une exception propriétaire
class MyError(Exception):
    pass

# trois méthodes
def f1(x):
    # on ne gère pas les exceptions - elles remontent automatiquement
    return f2(x)

def f2(y):
    # on ne gère pas les exceptions - elles remontent automatiquement
    return f3(y)

def f3(z):
    if (z % 2) == 0:
        # si z est pair, on lance une exception 
        raise MyError("exception dans f3")
    else:
        return 2*z

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

# les exceptions remontent la chaîne des méthodes appelées
# jusqu'à ce qu'une méthode l'intercepte. Ici ce sera main
try:
    print f1(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

# une méthode peut enrichir les exceptions qu'elle remonte
def f4(x):
    try:
        return f5(x)
    except MyError as infos:
        # on enrichit l'exception puis on la relance
        raise MyError(infos,"exception dans f4")

def f5(y):
    try:
        return f6(y)
    except MyError as infos:
        # on enrichit l'exception puis on la relance
        raise MyError(infos, "exception dans f5")

def f6(z):
    if (z % 2) == 0:
        # on lance une exception
        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)

Notes :

  • lignes 27-30, dans l'appel main --> f1 --> f2 --> f3 (ligne 28), l'exception MyError lancée par f3 va remonter jusqu'à main. Elle sera alors traitée par la clause except de la ligne 29 ;
  • lignes 55-58 : dans l'appel main --> f4 --> f5 --> f6 (ligne 56), l'exception lancée MyError par f6 va remonter jusqu'à main. Elle sera alors traitée par la clause except de la ligne 29. Cette fois-ci, dans sa remontée de la chaîne des méthodes appelantes, l'exception MyError est enrichie par des informations ajoutées par chaque méthode remontée.

Les résultats écran sont les suivants :

 
Sélectionnez
1.
2.
type :<class '__main__.MyError'>, arguments : exception dans f3
type :<class '__main__.MyError'>, arguments : (MyError(MyError('exception dans f6',), 'exception dans f5'), 'exception dans f4')

précédentsommairesuivant

  

Licence Creative Commons
Le contenu de cet article est rédigé par Serge Tahé et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.