Python Language
Des exceptions
Recherche…
Introduction
Les erreurs détectées lors de l'exécution s'appellent des exceptions et ne sont pas inconditionnellement fatales. La plupart des exceptions ne sont pas gérées par les programmes. il est possible d'écrire des programmes qui traitent les exceptions sélectionnées. Il existe des fonctionnalités spécifiques à Python pour gérer les exceptions et la logique des exceptions. De plus, les exceptions ont une hiérarchie de types enrichis, toutes héritant du type BaseException
.
Syntaxe
- lever l' exception
- raise # re-relance une exception qui a déjà été soulevée
- déclenche une exception de cause # Python 3 - définit la cause des exceptions
- déclenche une exception à partir de None # Python 3 - supprime tous les contextes d'exception
- essayer:
- sauf [types d'exception] [ comme identifiant ] :
- autre:
- enfin:
Augmenter les exceptions
Si votre code rencontre une condition qu'il ne sait pas gérer, telle qu'un paramètre incorrect, il doit déclencher l'exception appropriée.
def even_the_odds(odds):
if odds % 2 != 1:
raise ValueError("Did not get an odd number")
return odds + 1
Prendre des exceptions
Utilisez try...except:
pour attraper des exceptions. Vous devez spécifier une exception aussi précise que possible:
try:
x = 5 / 0
except ZeroDivisionError as e:
# `e` is the exception object
print("Got a divide by zero! The exception was:", e)
# handle exceptional case
x = 0
finally:
print "The END"
# it runs no matter what execute.
La classe d'exception spécifiée - dans ce cas, ZeroDivisionError
- ZeroDivisionError
toute exception ZeroDivisionError
cette classe ou à une sous-classe de cette exception.
Par exemple, ZeroDivisionError
est une sous-classe de ArithmeticError
:
>>> ZeroDivisionError.__bases__
(<class 'ArithmeticError'>,)
Et ainsi, les éléments suivants attraperont toujours le ZeroDivisionError
:
try:
5 / 0
except ArithmeticError:
print("Got arithmetic error")
Lancer le code de nettoyage avec finalement
Parfois, vous voudrez peut-être que quelque chose se produise, quelle que soit l'exception, par exemple, si vous devez nettoyer certaines ressources.
Le bloc finally
d'une clause try
se produira, que des exceptions aient été soulevées ou non.
resource = allocate_some_expensive_resource()
try:
do_stuff(resource)
except SomeException as e:
log_error(e)
raise # re-raise the error
finally:
free_expensive_resource(resource)
Ce modèle est souvent mieux géré avec les gestionnaires de contexte (en utilisant l'instruction with
).
Relancer les exceptions
Parfois, vous voulez attraper une exception simplement pour l'inspecter, par exemple à des fins de journalisation. Après l'inspection, vous souhaitez que l'exception continue à se propager comme auparavant.
Dans ce cas, utilisez simplement l'instruction raise
sans paramètres.
try:
5 / 0
except ZeroDivisionError:
print("Got an error")
raise
Gardez à l'esprit que quelqu'un plus haut dans la pile de l'appelant peut toujours attraper l'exception et la gérer d'une manière ou d'une autre. Le résultat final pourrait être une nuisance dans ce cas car cela se produira dans tous les cas (attrapé ou non attrapé). Donc, il serait peut-être préférable de soulever une exception différente, contenant votre commentaire sur la situation ainsi que l'exception initiale:
try:
5 / 0
except ZeroDivisionError as e:
raise ZeroDivisionError("Got an error", e)
Mais cela a l'inconvénient de réduire la trace d'exception à exactement cette raise
tandis que raise
sans argument conserve la trace d'exception d'origine.
En Python 3, vous pouvez conserver la pile d'origine en utilisant la syntaxe raise
- from
:
raise ZeroDivisionError("Got an error") from e
Chaîne d'exceptions avec augmentation de
Lors du traitement d'une exception, vous souhaiterez peut-être générer une autre exception. Par exemple, si vous obtenez une IOError
lors de la lecture d'un fichier, vous souhaiterez peut-être générer une erreur spécifique à l'application pour la présenter aux utilisateurs de votre bibliothèque.
Vous pouvez enchaîner les exceptions pour montrer comment le traitement des exceptions s'est déroulé:
>>> try:
5 / 0
except ZeroDivisionError as e:
raise ValueError("Division failed") from e
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: Division failed
Hiérarchie des exceptions
La gestion des exceptions se produit en fonction d'une hiérarchie d'exceptions, déterminée par la structure d'héritage des classes d'exception.
Par exemple, IOError
et OSError
sont deux sous-classes de EnvironmentError
. Le code qui IOError
une IOError
pas une OSError
. Toutefois, le code qui intercepte une EnvironmentError
intercepte à la fois IOError
s et OSError
s.
La hiérarchie des exceptions intégrées:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
Les exceptions sont des objets aussi
Les exceptions ne sont que des objets Python classiques qui héritent de BaseException
. Un script Python peut utiliser l'instruction raise
pour interrompre l'exécution, entraînant Python à imprimer une trace de pile de la pile d'appels à ce stade et une représentation de l'instance d'exception. Par exemple:
>>> def failing_function():
... raise ValueError('Example error!')
>>> failing_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in failing_function
ValueError: Example error!
qui dit qu'un ValueError
avec le message 'Example error!'
a été failing_function()
par notre failing_function()
, qui a été exécuté dans l’interpréteur.
Le code d'appel peut choisir de gérer tous les types d'exception qu'un appel peut générer:
>>> try:
... failing_function()
... except ValueError:
... print('Handled the error')
Handled the error
Vous pouvez mettre la main sur les objets d'exception en les affectant dans la partie except...
du code de gestion des exceptions:
>>> try:
... failing_function()
... except ValueError as e:
... print('Caught exception', repr(e))
Caught exception ValueError('Example error!',)
Une liste complète des exceptions Python intégrées avec leurs descriptions se trouve dans la documentation Python: https://docs.python.org/3.5/library/exceptions.html . Et voici la liste complète hiérarchisée: Hiérarchie des exceptions .
Création de types d'exception personnalisés
Créez une classe héritant d' Exception
:
class FooException(Exception):
pass
try:
raise FooException("insert description here")
except FooException:
print("A FooException was raised.")
ou un autre type d'exception:
class NegativeError(ValueError):
pass
def foo(x):
# function that only accepts positive values of x
if x < 0:
raise NegativeError("Cannot process negative numbers")
... # rest of function body
try:
result = foo(int(input("Enter a positive integer: "))) # raw_input in Python 2.x
except NegativeError:
print("You entered a negative number!")
else:
print("The result was " + str(result))
Ne pas attraper tout!
Bien qu'il soit souvent tentant d'attraper chaque Exception
:
try:
very_difficult_function()
except Exception:
# log / try to reconnect / exit gratiously
finally:
print "The END"
# it runs no matter what execute.
Ou même tout ce qui inclut BaseException
et tous ses enfants, y compris Exception
):
try:
even_more_difficult_function()
except:
pass # do whatever needed
Dans la plupart des cas, c'est une mauvaise pratique. Cela peut prendre plus de temps que prévu, comme SystemExit
, KeyboardInterrupt
et MemoryError
, chacun d'entre eux devant généralement être traité différemment des erreurs système ou logiques habituelles. Cela signifie également qu'il n'y a pas de compréhension claire de ce que le code interne peut faire de mal et comment récupérer correctement de cette condition. Si vous attrapez toutes les erreurs, vous ne saurez pas quelle erreur s'est produite ou comment y remédier.
Ceci est plus communément appelé «masquage de bogue» et devrait être évité. Laissez votre programme tomber en panne au lieu d'échouer silencieusement ou pire encore, en échouant à un niveau d'exécution plus profond. (Imaginez que c'est un système transactionnel)
Habituellement, ces constructions sont utilisées au niveau le plus externe du programme et enregistrent les détails de l'erreur afin que le bogue puisse être corrigé ou que l'erreur soit traitée de manière plus spécifique.
Prendre plusieurs exceptions
Il existe plusieurs moyens de détecter les exceptions multiples .
La première consiste à créer un tuple des types d'exception que vous souhaitez capturer et gérer de la même manière. Cet exemple entraînera le code à ignorer les exceptions KeyError
et AttributeError
.
try:
d = {}
a = d[1]
b = d.non_existing_field
except (KeyError, AttributeError) as e:
print("A KeyError or an AttributeError exception has been caught.")
Si vous souhaitez gérer différentes exceptions de différentes manières, vous pouvez fournir un bloc d'exception distinct pour chaque type. Dans cet exemple, nous interceptons toujours KeyError
et AttributeError
, mais gérons les exceptions de différentes manières.
try:
d = {}
a = d[1]
b = d.non_existing_field
except KeyError as e:
print("A KeyError has occurred. Exception message:", e)
except AttributeError as e:
print("An AttributeError has occurred. Exception message:", e)
Exemples pratiques de gestion des exceptions
Entrée utilisateur
Imaginez que vous souhaitiez qu'un utilisateur entre un numéro via une input
. Vous voulez vous assurer que l'entrée est un nombre. Vous pouvez utiliser try
/ except
ceci:
while True:
try:
nb = int(input('Enter a number: '))
break
except ValueError:
print('This is not a number, try again.')
Remarque: Python 2.x utiliserait plutôt raw_input
; la fonction input
existe dans Python 2.x mais a une sémantique différente. Dans l'exemple ci-dessus, input
accepterait également des expressions telles que 2 + 2
qui évaluent un nombre.
Si l'entrée n'a pas pu être convertie en entier, une valeur ValueError
est ValueError
. Vous pouvez l'attraper avec, except
. Si aucune exception est levée, la break
saute hors de la boucle. Après la boucle, nb
contient un entier.
Dictionnaires
Imaginez que vous itérez une liste d'entiers consécutifs, comme range(n)
, et vous avez une liste de dictionnaires d
qui contient des informations sur les choses à faire lorsque vous rencontrez des entiers particuliers, dites sauter le d[i]
ones suivantes.
d = [{7: 3}, {25: 9}, {38: 5}]
for i in range(len(d)):
do_stuff(i)
try:
dic = d[i]
i += dic[i]
except KeyError:
i += 1
Un KeyError
sera KeyError
lorsque vous essayez d'obtenir une valeur d'un dictionnaire pour une clé qui n'existe pas.
Autre
Le code dans un bloc else ne sera exécuté que si aucune exception n'a été déclenchée par le code dans le bloc try
. Ceci est utile si vous ne voulez pas exécuter du code si une exception est levée, mais vous ne voulez pas que les exceptions lancées par ce code soient interceptées.
Par exemple:
try:
data = {1: 'one', 2: 'two'}
print(data[1])
except KeyError as e:
print('key not found')
else:
raise ValueError()
# Output: one
# Output: ValueError
Notez que ce genre d' else:
ne peut pas être combiné avec un if
la clause elif
dans un elif
. Si vous avez un public if
elle doit rester en retrait en dessous d' else:
:
try:
...
except ...:
...
else:
if ...:
...
elif ...:
...
else:
...