Articles

Gestion des exceptions et des erreurs en Python

.

Introduction


Source

Avant d’aborder les raisons pour lesquelles la gestion des exceptions est essentielle et les types d’exceptions intégrées que supporte Python.d’exceptions intégrées que Python supporte, il est nécessaire de comprendre qu’il existe une différence subtile entre un error et un exception.

Les erreurs ne peuvent pas être gérées, alors que les exceptions Python peuvent être gérées au moment de l’exécution. Une erreur peut être une syntax (parsing), tandis qu’il peut y avoir de nombreux types d’exceptions qui pourraient se produire pendant l’exécution et qui ne sont pas inconditionnellement inopérables. Une Error pourrait indiquer des problèmes critiques qu’une application raisonnable ne devrait pas essayer d’attraper, tandis qu’une Exception pourrait indiquer des conditions qu’une application devrait essayer d’attraper. Les erreurs sont une forme d’exception non vérifiée et sont irrécupérables comme une OutOfMemoryError, qu’un programmeur ne devrait pas essayer de gérer.

La gestion des exceptions rend votre code plus robuste et aide à prévenir les défaillances potentielles qui provoqueraient l’arrêt incontrôlé de votre programme. Imaginez si vous avez écrit un code qui est déployé en production et que malgré tout, il se termine à cause d’une exception, votre client n’apprécierait pas cela, il vaut donc mieux gérer l’exception particulière à l’avance et éviter le chaos.

Les erreurs peuvent être de différents types :

  • Erreur de syntaxe
  • Erreur de mémoire
  • Erreur de récursion
  • Exceptions

Voyons-les une par une.

Erreur de syntaxe

Les erreurs de syntaxe souvent appelées erreurs d’analyse, sont principalement causées lorsque l’analyseur syntaxique détecte un problème de syntaxe dans votre code.

Prenons un exemple pour le comprendre.

a = 8b = 10c = a b
 File "<ipython-input-8-3b3ffcedf995>", line 3 c = a b ^SyntaxError: invalid syntax

La flèche ci-dessus indique que l’analyseur syntaxique a rencontré une erreur lors de l’exécution du code. Le jeton précédant la flèche provoque l’échec. Pour rectifier des erreurs aussi fondamentales, Python fera le gros du travail puisqu’il imprimera pour vous le nom du fichier et le numéro de la ligne à laquelle l’erreur s’est produite.

Erreur de mémoire

Les erreurs de mémoire dépendent surtout de la RAM de vos systèmes et sont liées à Heap. Si vous avez de gros objets (ou) des objets référencés en mémoire, alors vous verrez OutofMemoryError (Source). Cela peut être causé en raison de diverses raisons :

  • Utilisation d’une architecture Python 32 bits (l’allocation maximale de mémoire donnée est très faible, entre 2GB – 4GB).
  • Chargement d’un très gros fichier de données
  • Exécution d’un modèle d’apprentissage automatique/apprentissage profond et bien d’autres encore.

Vous pouvez gérer l’erreur memory à l’aide du traitement des exceptions, une exception de repli pour les cas où l’interpréteur manque entièrement de mémoire et doit immédiatement arrêter l’exécution en cours. Dans ces rares cas, Python lève une OutofMemoryError, permettant au script de se catch en quelque sorte et break hors de l’erreur de mémoire et de se rétablir.

Mais comme Python adopte l’architecture de gestion de la mémoire du langage C (fonction malloc()), il n’est pas certain que tous les processus du script se rétablissent – dans certains cas, une MemoryError entraînera un crash irrécupérable. Par conséquent, l’utilisation de la gestion des exceptions pour une telle erreur n’est ni une bonne pratique, ni conseillée.

Erreur de récursion

Elle est liée à stack et se produit lorsque vous appelez des fonctions. Comme son nom l’indique, l’erreur recursion transpire lorsque trop de méthodes, l’une dans l’autre, sont exécutées (une avec une récursion infinie), ce qui est limité par la taille de la pile.

Toutes vos variables locales et les données associées aux appels de méthodes seront placées sur la pile. Pour chaque appel de méthode, un cadre de pile sera créé, et les données locales ainsi que les données associées aux appels de méthode seront placées à l’intérieur de ce cadre de pile. Une fois l’exécution de la méthode terminée, le cadre de pile sera supprimé.

Pour reproduire cette erreur, définissons une fonction recursion qui sera récursive, c’est-à-dire qu’elle continuera à s’appeler elle-même comme un appel de méthode en boucle infinie, vous verrez un StackOverflow ou une erreur de récursion car le cadre de pile sera peuplé de données de méthode pour chaque appel, mais il ne sera pas libéré.

def recursion(): return recursion()
recursion()
---------------------------------------------------------------------------RecursionError Traceback (most recent call last)<ipython-input-3-c6e0f7eb0cde> in <module>----> 1 recursion()<ipython-input-2-5395140f7f05> in recursion() 1 def recursion():----> 2 return recursion()... last 1 frames repeated, from the frame below ...<ipython-input-2-5395140f7f05> in recursion() 1 def recursion():----> 2 return recursion()RecursionError: maximum recursion depth exceeded

Erreur d’indentation

L’erreur d’indentation est similaire dans son esprit à l’erreur de syntaxe et en relève. Cependant, spécifique aux seuls problèmes liés à l’indentation dans le script.

Prenons donc un exemple rapide pour comprendre une erreur d’indentation.

for i in range(10):print('Hello world')
 File "<ipython-input-6-628f419d2da8>", line 2 print('Hello world') ^IndentationError: expected an indented block

Exceptions

Même si la syntaxe d’une déclaration ou d’une expression est correcte, elle peut tout de même provoquer une erreur lors de son exécution. Les exceptions Python sont des erreurs qui sont détectées pendant l’exécution et qui ne sont pas inconditionnellement fatales : vous apprendrez bientôt dans le tutoriel comment les gérer dans les programmes Python. Un objet exception est créé lorsqu’un script Python lève une exception. Si le script ne gère pas explicitement l’exception, le programme sera obligé de se terminer brusquement.

Les programmes ne gèrent généralement pas les exceptions, et donnent lieu à des messages d’erreur comme ceux présentés ici :

Erreur de type

a = 2b = 'DataCamp'a + b
---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-7-86a706a0ffdf> in <module> 1 a = 2 2 b = 'DataCamp'----> 3 a + bTypeError: unsupported operand type(s) for +: 'int' and 'str'

Erreur de division par zéro

100 / 0
---------------------------------------------------------------------------ZeroDivisionError Traceback (most recent call last)<ipython-input-43-e9e866a10e2a> in <module>----> 1 100 / 0ZeroDivisionError: division by zero

Il existe différents types d’exceptions Python, et le type est imprimé dans le cadre du message : les types dans les deux exemples ci-dessus sont ZeroDivisionError et TypeError. Les deux chaînes d’erreur imprimées comme le type d’exception est le nom de l’exception intégrée de Python.

Le reste de la ligne d’erreur fournit les détails de ce qui a provoqué l’erreur en fonction du type d’exception.

Regardons maintenant les exceptions intégrées de Python.

Exceptions intégrées


Source

Avant de commencer à apprendre les exceptions intégrées, révisons rapidement les quatre principaux composants de la gestion des exceptions, comme le montre cette figure.

  • Essayer : Il exécutera le bloc de code dans lequel vous vous attendez à ce qu’une erreur se produise.
  • Sauf : Ici, vous définirez le type d’exception que vous attendez dans le bloc try (intégré ou personnalisé).
  • Autre : S’il n’y a pas d’exception, alors ce bloc de code sera exécuté (considérez cela comme un remède ou une option de repli si vous vous attendez à ce qu’une partie de votre script produise une exception).
  • Enfin : Indépendamment de la présence d’une exception ou non, ce bloc de code sera toujours exécuté.

Dans la section suivante du tutoriel, vous découvrirez les types d’exceptions les plus courants et vous apprendrez également à les traiter à l’aide de la gestion des exceptions.

Erreur d’interruption de clavier

L’exception KeyboardInterrupt est levée lorsque vous essayez d’arrêter un programme en cours d’exécution en appuyant sur ctrl+c ou ctrl+z dans une ligne de commande ou en interrompant le noyau dans Jupyter Notebook. Parfois, vous n’avez peut-être pas l’intention d’interrompre un programme, mais par erreur, cela se produit, auquel cas l’utilisation de la gestion des exceptions pour éviter de tels problèmes peut être utile.

Dans l’exemple ci-dessous, si vous exécutez la cellule et interrompez le noyau, le programme lèvera une exception KeyboardInterrupt.inp = input()Gérons maintenant l’exception KeyboardInterrupt.

try: inp = input() print ('Press Ctrl+C or Interrupt the Kernel:')except KeyboardInterrupt: print ('Caught KeyboardInterrupt')else: print ('No exception occurred')
Caught KeyboardInterrupt

Erreur standard

Apprenons à connaître certaines des erreurs standard qui pourraient généralement se produire pendant la programmation.

Erreur arithmétique

  • Erreur de division zéro
  • Erreur de débordement
  • Erreur de point flottant

Toutes les exceptions ci-dessus relèvent de la classe de base Arithmetic et sont soulevées pour des erreurs dans les opérations arithmétiques, comme nous le verrons ici.

Division nulle

Lorsque le diviseur (deuxième argument de la division) ou le dénominateur est nul, alors la résultante soulève une erreur de division nulle.

try: a = 100 / 0 print (a)except ZeroDivisionError: print ("Zero Division Exception Raised." )else: print ("Success, no error!")
Zero Division Exception Raised.

Erreur de dépassement

L’erreur de dépassement est soulevée lorsque le résultat d’une opération arithmétique est hors limites. OverflowError est soulevé pour les entiers qui sont en dehors d’une plage requise.

try: import math print(math.exp(1000))except OverflowError: print ("OverFlow Exception Raised.")else: print ("Success, no error!")
OverFlow Exception Raised.

Assertion Error

Lorsqu’une instruction d’assertion échoue, une Assertion Error est soulevée.

Prenons un exemple pour comprendre l’erreur d’assertion. Disons que vous avez deux variables a et b, que vous devez comparer. Pour vérifier si a et b sont égaux ou non, vous appliquez un assert mot-clé avant, qui soulèvera une Assertion exception lorsque l’expression retournera false.

try: a = 100 b = "DataCamp" assert a == bexcept AssertionError: print ("Assertion Exception Raised.")else: print ("Success, no error!")

Assertion Exception Raised.

Erreur d’attribut

Lorsqu’un attribut inexistant est référencé, et que cette référence ou cette affectation d’attribut échoue, une erreur d’attribut est levée.

Dans l’exemple ci-dessous, vous pouvez observer que l’objet de classe Attributes ne possède pas d’attribut portant le nom attribute.

class Attributes(object): a = 2 print (a)try: object = Attributes() print (object.attribute)except AttributeError: print ("Attribute Exception Raised.")
2Attribute Exception Raised.

Erreur d’importation

L’erreur d’importation est levée lorsque vous essayez d’importer un module qui n’existe pas (incapable de se charger) dans son chemin standard ou même lorsque vous faites une faute de frappe dans le nom du module.

import nibabel
---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)<ipython-input-6-9e567e3ae964> in <module>----> 1 import nibabelModuleNotFoundError: No module named 'nibabel'

Lookup Error

Lookup Error agit comme une classe de base pour les exceptions qui se produisent lorsqu’une clé ou un index utilisé sur un mapping ou une séquence d’une liste/dictionnaire n’est pas valide ou n’existe pas.

Les deux types d’exceptions soulevées sont :

  • IndexError
  • KeyError

Key Error

Si une clé à laquelle vous essayez d’accéder est introuvable dans le dictionnaire, une key exception d’erreur est soulevée.

try: a = {1:'a', 2:'b', 3:'c'} print (a) except LookupError: print ("Key Error Exception Raised.")else: print ("Success, no error!")
Key Error Exception Raised.

Erreur d’index

Lorsque vous essayez d’accéder à un index (séquence) d’une liste qui n’existe pas dans cette liste ou qui est hors de portée de cette liste, une erreur d’index est levée.

try: a = print (a) except LookupError: print ("Index Error Exception Raised, list index out of range")else: print ("Success, no error!")
Index Error Exception Raised, list index out of range

Erreur de mémoire

Comme nous l’avons vu précédemment, une erreur de mémoire est soulevée lorsqu’une opération n’obtient pas suffisamment de mémoire pour poursuivre son traitement.

Erreur de nom

L’erreur de nom est soulevée lorsqu’un nom local ou global n’est pas trouvé.

Dans l’exemple ci-dessous, la variable ans n’est pas définie. Par conséquent, vous obtiendrez une name error.

try: print (ans)except NameError: print ("NameError: name 'ans' is not defined")else: print ("Success, no error!")
NameError: name 'ans' is not defined

Runtime Error

Not Implemented Error

Cette section du tutoriel est dérivée de cette Source. Runtime Error agit comme une classe de base pour NotImplemented Error. Les méthodes abstraites des classes définies par l’utilisateur doivent lever cette exception lorsque les classes dérivées surchargent la méthode.

class BaseClass(object): """Defines the interface""" def __init__(self): super(BaseClass, self).__init__() def do_something(self): """The interface, not implemented""" raise NotImplementedError(self.__class__.__name__ + '.do_something')class SubClass(BaseClass): """Implementes the interface""" def do_something(self): """really does something""" print (self.__class__.__name__ + ' doing something!')SubClass().do_something()BaseClass().do_something()
SubClass doing something!---------------------------------------------------------------------------NotImplementedError Traceback (most recent call last)<ipython-input-1-57792b6bc7e4> in <module> 14 15 SubClass().do_something()---> 16 BaseClass().do_something()<ipython-input-1-57792b6bc7e4> in do_something(self) 5 def do_something(self): 6 """The interface, not implemented"""----> 7 raise NotImplementedError(self.__class__.__name__ + '.do_something') 8 9 class SubClass(BaseClass):NotImplementedError: BaseClass.do_something

Type Error

L’exceptionType Error est levée lorsque deux types d’opérandes ou d’objets différents ou non liés sont combinés.

Dans l’exemple ci-dessous, un entier et une chaîne de caractères sont ajoutés, ce qui entraîne une erreur type.

try: a = 5 b = "DataCamp" c = a + bexcept TypeError: print ('TypeError Exception Raised')else: print ('Success, no error!')
TypeError Exception Raised

Erreur de valeur

L’erreur de valeur est soulevée lorsque l’opération intégrée ou une fonction reçoit un argument qui a une type correcte mais une value invalide.

Dans l’exemple ci-dessous, l’opération intégrée float reçoit un argument, qui est une séquence de caractères (valeur), ce qui est invalide pour un type float.

try: print (float('DataCamp'))except ValueError: print ('ValueError: could not convert string to float: \'DataCamp\'')else: print ('Success, no error!')
ValueError: could not convert string to float: 'DataCamp'

Exceptions personnalisées Python

Cette section du tutoriel est dérivée de cette Source.

Comme étudié dans la section précédente du tutoriel, Python possède de nombreuses exceptions intégrées que vous pouvez utiliser dans votre programme. Pourtant, parfois, vous pouvez avoir besoin de créer des exceptions personnalisées avec des messages personnalisés pour servir votre objectif.

Vous pouvez y parvenir en créant une nouvelle classe, qui sera dérivée de la classe Exception prédéfinie dans Python.

class UnAcceptedValueError(Exception): def __init__(self, data): self.data = data def __str__(self): return repr(self.data)Total_Marks = int(input("Enter Total Marks Scored: "))try: Num_of_Sections = int(input("Enter Num of Sections: ")) if(Num_of_Sections < 1): raise UnAcceptedValueError("Number of Sections can't be less than 1")except UnAcceptedValueError as e: print ("Received error:", e.data)
Enter Total Marks Scored: 10Enter Num of Sections: 0Received error: Number of Sections can't be less than 1

Dans l’exemple ci-dessus, comme vous l’avez observé, si vous entrez quelque chose de moins que 1, une exception personnalisée sera levée et gérée. De nombreux modules standard définissent leurs exceptions pour signaler les erreurs qui peuvent survenir dans les fonctions qu’ils définissent.

Les démérites du traitement des exceptions Python

L’utilisation du traitement des exceptions Python a également un effet secondaire. Comme, les programmes qui font usage des blocs try-except pour gérer les exceptions s’exécuteront légèrement plus lentement, et la taille de votre code augmentera.

Vous trouverez ci-dessous un exemple où le module timeit de Python est utilisé pour vérifier le temps d’exécution de 2 déclarations différentes. Dans stmt1, on utilise try-except pour gérer ZeroDivisionError, tandis que dans stmt2, on utilise l’instruction if comme condition de vérification normale. Ensuite, vous exécutez ces instructions 10000 fois avec la variable a=0. Le point à noter ici est que le temps d’exécution des deux instructions est différent. Vous constaterez que stmt1, qui gère l’exception, a pris un temps légèrement plus long que stmt2, qui ne fait que vérifier la valeur et ne fait rien si la condition n’est pas remplie.

Hence, vous devriez limiter l’utilisation de la gestion des exceptions Python et l’utiliser uniquement pour des cas rares. Par exemple, lorsque vous n’êtes pas sûr que l’entrée sera un entier ou un flottant pour les calculs arithmétiques ou que vous n’êtes pas sûr de l’existence d’un fichier en essayant de l’ouvrir.

import timeitsetup="a=0"stmt1 = '''\try: b=10/aexcept ZeroDivisionError: pass'''
stmt2 = '''\if a!=0: b=10/a'''
print("time=",timeit.timeit(stmt1,setup,number=10000))print("time=",timeit.timeit(stmt2,setup,number=10000))
time= 0.003897680000136461time= 0.0002797570000439009

Félicitations pour avoir terminé ce tutoriel.

Comme vous l’avez appris, la gestion des exceptions permet de briser le flux de contrôle typique de votre programme en fournissant un mécanisme de découplage de la gestion des erreurs Python et rend votre code plus robuste.

La gestion des exceptions Python est l’un des principaux facteurs permettant de rendre votre code prêt pour la production et à l’épreuve du futur, en dehors de l’ajout de tests unitaires et de la programmation orientée objet.

C’est une technique puissante et un concept de seulement quatre blocs. Le bloc try recherche les exceptions lancées par le code, tandis que le bloc except gère ces exceptions (intégrées et personnalisées).

Un bon exercice pour vous tous serait d’utiliser les quatre composantes du traitement exceptionnel et d’essayer de rendre votre code plus robuste.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *