Introduction
Bien que les exceptions aient été introduites plus tôt, cette section complète quelques détails supplémentaires sur la vérification d'erreurs et la gestion d'exceptions.
This tutorial is from open-source community. Access the source code
💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici
Bien que les exceptions aient été introduites plus tôt, cette section complète quelques détails supplémentaires sur la vérification d'erreurs et la gestion d'exceptions.
Python ne effectue aucune vérification ni validation des types ou des valeurs des arguments de fonction. Une fonction fonctionnera avec n'importe quel données compatible avec les instructions de la fonction.
def add(x, y):
return x + y
add(3, 4) ## 7
add('Hello', 'World') ## 'HelloWorld'
add('3', '4') ## '34'
Si des erreurs se produisent dans une fonction, elles apparaissent au moment de l'exécution (en tant qu'exception).
def add(x, y):
return x + y
>>> add(3, '4')
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +:
'int' and 'str'
>>>
Pour vérifier le code, il y a une forte mise sur les tests (traité plus tard).
Les exceptions sont utilisées pour signaler des erreurs. Pour lever une exception vous-même, utilisez l'instruction raise
.
if name not in authorized:
raise RuntimeError(f'{name} not authorized')
Pour attraper une exception, utilisez try-except
.
try:
authenticate(username)
except RuntimeError as e:
print(e)
Les exceptions se propagent jusqu'au premier except
correspondant.
def grok():
...
raise RuntimeError('Whoa!') ## Exception levée ici
def spam():
grok() ## Appel qui entraînera une exception
def bar():
try:
spam()
except RuntimeError as e: ## Exception capturée ici
...
def foo():
try:
bar()
except RuntimeError as e: ## L'exception n'arrive PAS ici
...
foo()
Pour gérer l'exception, placez des instructions dans le bloc except
. Vous pouvez ajouter n'importe quelles instructions pour gérer l'erreur.
def grok():...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Exception capturée ici
instructions ## Utilisez ces instructions
instructions
...
bar()
Après la gestion, l'exécution reprend avec la première instruction après le try-except
.
def grok():...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Exception capturée ici
instructions
instructions
...
instructions ## Reprend l'exécution ici
instructions ## Et continue ici
...
bar()
Il existe environ une vingtaine d'exceptions intégrées. Généralement, le nom de l'exception indique ce qui ne va pas (par exemple, une ValueError
est levée parce que vous avez fourni une mauvaise valeur). Ce n'est pas une liste exhaustive. Consultez la documentation pour en savoir plus.
ArithmeticError
AssertionError
EnvironmentError
EOFError
ImportError
IndexError
KeyboardInterrupt
KeyError
MemoryError
NameError
ReferenceError
RuntimeError
SyntaxError
SystemError
TypeError
ValueError
Les exceptions ont une valeur associée. Elle contient des informations plus spécifiques sur ce qui ne va pas.
raise RuntimeError('Invalid user name')
Cette valeur est partie de l'instance d'exception qui est placée dans la variable fournie à except
.
try:
...
except RuntimeError as e: ## `e` contient l'exception levée
...
e
est une instance du type d'exception. Cependant, elle semble souvent une chaîne de caractères lorsqu'elle est affichée.
except RuntimeError as e:
print('Failed : Reason', e)
Vous pouvez capturer différents types d'exceptions en utilisant plusieurs blocs except
.
try:
...
except LookupError as e:
...
except RuntimeError as e:
...
except IOError as e:
...
except KeyboardInterrupt as e:
...
Alternativement, si les instructions pour les gérer sont les mêmes, vous pouvez les regrouper :
try:
...
except (IOError,LookupError,RuntimeError) as e:
...
Pour capturer n'importe quelle exception, utilisez Exception
comme ceci :
try:
...
except Exception: ## DANGER. Voir ci-dessous
print('An error occurred')
En général, écrire du code de cette manière est une mauvaise idée car vous n'aurez aucune idée du pourquoi de son échec.
Voici la mauvaise manière d'utiliser les exceptions.
try:
go_do_something()
except Exception:
print('Computer says no')
Cela capture toutes les erreurs possibles et peut rendre impossible le débogage lorsque le code échoue pour une raison que vous n'attendiez absolument pas (par exemple, un module Python non installé, etc.).
Si vous allez capturer toutes les erreurs, c'est une approche plus raisonnable.
try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)
Il indique une raison spécifique pour l'échec. Il est presque toujours une bonne idée d'avoir un mécanisme pour visualiser / signaler les erreurs lorsque vous écrivez du code qui capture toutes les exceptions possibles.
En général cependant, il est préférable de capturer l'erreur de la manière la plus limitée possible. Capturer seulement les erreurs que vous pouvez réellement gérer. Laissez les autres erreurs passer - peut-être que du code autre peut les gérer.
Utilisez raise
pour propager une erreur capturée.
try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)
raise
Cela vous permet de prendre des mesures (par exemple, journalisation) et de transmettre l'erreur à l'appelant.
Ne capturez pas les exceptions. Échoue rapidement et fortement. Si c'est important, quelqu'un d'autre s'occupera du problème. Capturez une exception seulement si vous êtes celui-là. C'est-à-dire, capturez seulement les erreurs où vous pouvez récupérer et continuer de manière raisonnable.
finally
Elle spécifie le code qui doit s'exécuter que l'exception se produise ou non.
lock = Lock()
...
lock.acquire()
try:
...
finally:
lock.release() ## ceci sera TOUJOURS exécuté. Avec ou sans exception.
Utilisé couramment pour gérer de manière sûre les ressources (en particulier les verrous, les fichiers, etc.).
with
Dans le code moderne, try-finally
est souvent remplacé par l'instruction with
.
lock = Lock()
with lock:
## verrou acquis
...
## verrou libéré
Un exemple plus familier :
with open(filename) as f:
## Utiliser le fichier
...
## Fichier fermé
with
définit un contexte d'utilisation pour une ressource. Lorsque l'exécution quitte ce contexte, les ressources sont libérées. with
ne fonctionne qu'avec certains objets qui ont été spécifiquement programmés pour le supporter.
La fonction parse_csv()
que vous avez écrite dans la section précédente permet de sélectionner des colonnes spécifiées par l'utilisateur, mais cela ne fonctionne que si le fichier de données d'entrée a des en-têtes de colonne.
Modifiez le code de manière à ce qu'une exception soit levée si les arguments select
et has_headers=False
sont tous les deux passés. Par exemple :
>>> parse_csv('/home/labex/project/prices.csv', select=['name','price'], has_headers=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 9, in parse_csv
raise RuntimeError("select argument requires column headers")
RuntimeError: select argument requires column headers
>>>
Ayant ajouté ce contrôle, vous pourriez vous demander si vous devriez effectuer d'autres types de vérifications de cohérence dans la fonction. Par exemple, devriez-vous vérifier que le nom de fichier est une chaîne de caractères, que types est une liste, ou quelque chose de ce genre?
En règle générale, il est généralement préférable de sauter de telles vérifications et de laisser le programme échouer sur des entrées erronées. Le message de traceback indiquera la source du problème et peut aider à déboguer.
La principale raison d'ajouter le contrôle ci-dessus est d'éviter d'exécuter le code dans un mode absurde (par exemple, en utilisant une fonctionnalité qui nécessite des en-têtes de colonne, mais en spécifiant simultanément qu'il n'y a pas d'en-têtes).
Cela indique une erreur de programmation de la part du code appelant. Vérifier les cas qui "ne devraient pas arriver" est souvent une bonne idée.
La fonction parse_csv()
que vous avez écrite est utilisée pour traiter le contenu complet d'un fichier. Cependant, dans le monde réel, il est possible que les fichiers d'entrée soient corrompus, manquants ou contiennent des données sales. Essayez cet exemple :
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 36, in parse_csv
row = [func(val) for func, val in zip(types, row)]
ValueError: invalid literal for int() with base 10: ''
>>>
Modifiez la fonction parse_csv()
pour capturer toutes les exceptions ValueError
générées pendant la création d'un enregistrement et afficher un message d'avertissement pour les lignes qui ne peuvent pas être converties.
Le message devrait inclure le numéro de ligne et des informations sur la raison pour laquelle la conversion a échoué. Pour tester votre fonction, essayez de lire le fichier missing.csv
ci-dessus. Par exemple :
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Ligne 4: Impossible de convertir ['MSFT', '', '51.23']
Ligne 4: Raison : invalid literal for int() with base 10: ''
Ligne 7: Impossible de convertir ['IBM', '', '70.44']
Ligne 7: Raison : invalid literal for int() with base 10: ''
>>>
>>> portfolio
[{'name': 'AA','shares': 100, 'price': 32.2}, {'name': 'IBM','shares': 50, 'price': 91.1}, {'name': 'CAT','shares': 150, 'price': 83.44}, {'name': 'GE','shares': 95, 'price': 40.37}, {'name': 'MSFT','shares': 50, 'price': 65.1}]
>>>
Modifiez la fonction parse_csv()
de sorte que les messages d'erreur de l'analyse puissent être silencés si l'utilisateur le souhaite explicitement. Par exemple :
>>> portfolio = parse_csv('missing.csv', types=[str,int,float], silence_errors=True)
>>> portfolio
[{'name': 'AA','shares': 100, 'price': 32.2}, {'name': 'IBM','shares': 50, 'price': 91.1}, {'name': 'CAT','shares': 150, 'price': 83.44}, {'name': 'GE','shares': 95, 'price': 40.37}, {'name': 'MSFT','shares': 50, 'price': 65.1}]
>>>
La gestion des erreurs est l'une des choses les plus difficiles à bien faire dans la plupart des programmes. En règle générale, vous ne devriez pas ignorer silencieusement les erreurs. Au contraire, il est préférable de signaler les problèmes et de donner à l'utilisateur l'option de silencer le message d'erreur s'il le souhaite.
Félicitations ! Vous avez terminé le laboratoire de vérification d'erreurs. Vous pouvez pratiquer d'autres laboratoires sur LabEx pour améliorer vos compétences.