Algo. et prog. 2 en
Logo de Python
Arnaud COUTURIER - Python 3.10

Les exceptions

Jusqu'ici, lorsqu'on a des erreurs dans nos programmes, on se contente de les lire, de les comprendre, et enfin de corriger la source du problème. Il y a au moins deux types d'erreurs à distinguer : les erreurs de syntaxe et les exceptions.

Erreurs de syntaxe

Les erreurs de syntaxe, qui sont des erreurs d'analyse du code, sont peut-être celles que vous rencontrez le plus souvent lorsque vous êtes encore en phase d'apprentissage de Python. Ces erreurs arrivent quand vous avez mal écrit le code Python, votre code ne suit pas strictement la syntaxe de Python. Elles sont en général faciles à corriger.

Par exemple le fichier mon_fichier.py contient le code ci-dessous avec une erreur de syntaxe:


		if 0 < 1
			print("Coucou")
	

File "C:\Users\Arnaud\Desktop\mon_fichier.py", line 1
    if 0 < 1
            ^
SyntaxError: expected ':'

L'analyseur syntaxique de Python indique le fichier exact et la ligne concernés par l'erreur et affiche une petite « flèche » pointant vers le premier endroit de la ligne où l'erreur a été détectée. Dans cet exemple la flèche et le texte d'erreur indiquent qu'il manque deux points (':').

Exceptions

Même si une instruction ou une expression est syntaxiquement correcte, elle peut générer une erreur lors de son exécution. Les erreurs détectées durant l'exécution sont appelées des exceptions et ne sont pas toujours fatales : nous apprendrons bientôt comment les traiter dans vos programmes. La plupart des exceptions toutefois ne sont pas prises en charge par les programmes, ce qui génère des messages d'erreurs de ce genre :


		1 / 0
	

Traceback (most recent call last):
  File "C:\Users\Arnaud\Desktop\mon_fichier.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero


		a + 1
	

Traceback (most recent call last):
  File "C:\Users\Arnaud\Desktop\mon_fichier.py", line 2, in <module>
    a + 1
NameError: name 'a' is not defined


		"1" + 1
	

Traceback (most recent call last):
  File "C:\Users\Arnaud\Desktop\mon_fichier.py", line 2, in <module>
    "1" + 1
TypeError: can only concatenate str (not "int") to str

La dernière ligne du message d'erreur explique brievement quelle est l'erreur. Les exceptions peuvent être de différents types, et ce type est indiqué juste avant le message : dans ces exemple les types des erreurs sont ZeroDivisionError, NameError et TypeError. Le type est aussi la classe de l'exception.

Gestion des exceptions avec try-except

Il est possible d'écrire des programmes qui prennent en charge les types d'exceptions qu'on souhaite, pour ensuite faire les actions qui s'imposent dans le cas où elles surviennent, grâce à la structure try-except. Exemple:


		print("Avant")
		try:
			1 / 0
			print("La division a fonctionné")
		except ZeroDivisionError:
			print("Une division par zéro a eu lieu, ouille!")
		print("Après")
	
Avant
Une division par zéro a eu lieu, ouille!
Après

Remarquez ↑ que la ligne 4 n'a pas été exécutée, car la 3 a provoqué une ZeroDivisionError. Par contre la ligne 6 a été exécutée. Maintenant si on corrige la source de l'erreur, c'est-à-dire la division par zéro:


		print("Avant")
		try:
			1 / 1
			print("La division a fonctionné")
		except ZeroDivisionError:
			print("Une division par zéro a eu lieu, ouille!")
		print("Après")
	
Avant
La division a fonctionné
Après

Maintenant ↑ la ligne 4 a été exécutée mais pas la 6, car la division de la ligne 3 n'a provoqué aucune erreur.

On peut avoir plusieurs except, comme ceci:


		try:
			1 / 1
			print("La division a fonctionné")
			a - 1
			print("La soustraction a fonctionné")
			"1" + 1
			print("L'addition a fonctionné")
			a.methode_inexistante()
			print("L'appel de méthode a réussi")
		except ZeroDivisionError:
			print("Une division par zéro a eu lieu, ouille!")
		except NameError:
			print("Ben alors, on a oublié de définir l'identifiant a?")
		except TypeError:
			print("On ne concatène pas une chaîne avec un entier, il faut convertir d'abord!")
	
La division a fonctionné
Ben alors, on a oublié de définir l'identifiant a?

Si on définit l'identifiant a avant de l'utiliser (on peut le faire avant ou dans le bloc try):


		a = 2
		try:
			1 / 1
			print("La division a fonctionné")
			a - 1
			print("La soustraction a fonctionné")
			"1" + 1
			print("L'addition a fonctionné")
			a.methode_inexistante()
			print("L'appel de méthode a réussi")
		except ZeroDivisionError:
			print("Une division par zéro a eu lieu, ouille!")
		except NameError:
			print("Ben alors, on a oublié de définir l'identifiant a?")
		except TypeError:
			print("On ne concatène pas une chaîne avec un entier, il faut convertir d'abord!")
	
La division a fonctionné
La soustraction a fonctionné
On ne concatène pas une chaîne avec un entier, il faut convertir d'abord!

Et si on fait fonctionner la concaténation, on obtient:


		a = 2
		try:
			1 / 1
			print("La division a fonctionné")
			a - 1
			print("La soustraction a fonctionné")
			"1" + str(1)
			print("L'addition a fonctionné")
			a.methode_inexistante()
			print("L'appel de méthode a réussi")
		except ZeroDivisionError:
			print("Une division par zéro a eu lieu, ouille!")
		except NameError:
			print("Ben alors, on a oublié de définir l'identifiant a?")
		except TypeError:
			print("On ne concatène pas une chaîne avec un entier, il faut convertir d'abord!")
	
La division a fonctionné
La soustraction a fonctionné
L'addition a fonctionné
Traceback (most recent call last):
  File "C:\Users\Arnaud\Desktop\mon_fichier.py", line 9, in <module>
    a.methode_inexistante()
AttributeError: 'int' object has no attribute 'methode_inexistante'

Comme aucun except ne correspond au type d'exception AttributeError, alors l'exception n'est pas gérée par notre programme, qui crashe, car l'objet référencé par a est de type int, et tout objet de ce type n'a pas de méthode methode_inexistante().

On peut ajouter un except générique qui prend en charge toutes les exceptions peu importe leur type. Les except spécifiques sont prioritaires: si une exception intervient dans le try et qu'elle n'est pas listée parmi les except spécifiques, c'est le except générique qui est utilisé. L'ordre des except dans une structure try-except n'a pas d'importance.


		a = 2
		try:
			1 / 1
			print("La division a fonctionné")
			a - 1
			print("La soustraction a fonctionné")
			"1" + str(1)
			print("L'addition a fonctionné")
			a.methode_inexistante()
			print("L'appel de méthode a réussi")
		except ZeroDivisionError:
			print("Une division par zéro a eu lieu, ouille!")
		except NameError:
			print("Ben alors, on a oublié de définir l'identifiant a?")
		except TypeError:
			print("On ne concatène pas une chaîne avec un entier, il faut convertir d'abord!")
		except:
			print("Une erreur d'un autre type est apparue... que faire?")
	
La division a fonctionné
La soustraction a fonctionné
L'addition a fonctionné
Une erreur d'un autre type est apparue... que faire?

L'instruction try fonctionne comme ceci :

Une structure try-except peut comporter plusieurs clauses except pour permettre la prise en charge de différentes exceptions. Mais un seul except, au plus, sera exécuté. Les except ne prennent en charge que les exceptions qui interviennent dans la clause try correspondante

if-else VS try-except

Il ne faut pas confondre les deux.

if-else sert à différentier deux situations différentes à partir du test d'une condition, et à agir en conséquence, au cours du déroulement normal du programme.

try-except en revanche sert à différentier deux situations non pas à partir d'un test, mais la situation où le programme se déroule sans erreur, et la situation où le programme rencontre une erreur.