Comme vous l'avez bien deviné, il y a deux côtés: intercepter toute erreur en ne spécifiant aucun type d'exception après except
, et simplement le passer sans prendre aucune action.
Mon explication est «un peu» plus longue - donc tl; dr, cela se résume à ceci:
- N'attrapez aucune erreur . Spécifiez toujours les exceptions à partir desquelles vous êtes prêt à récupérer et interceptez uniquement celles-ci.
- Essayez d'éviter de passer à l'exception des blocs . À moins que cela ne soit explicitement souhaité, ce n'est généralement pas un bon signe.
Mais entrons dans les détails:
Ne détectez aucune erreur
Lorsque vous utilisez un try
bloc, vous le faites généralement parce que vous savez qu'il y a une chance qu'une exception soit levée. En tant que tel, vous avez également déjà une idée approximative de ce qui peut se casser et de quelle exception peut être levée. Dans de tels cas, vous interceptez une exception car vous pouvez vous en remettre positivement . Cela signifie que vous êtes prêt pour l'exception et que vous avez un plan alternatif que vous suivrez en cas d'exception.
Par exemple, lorsque vous demandez à l'utilisateur de saisir un nombre, vous pouvez convertir l'entrée en utilisant int()
ce qui pourrait élever un ValueError
. Vous pouvez facilement récupérer cela en demandant simplement à l'utilisateur de réessayer, donc attraper le ValueError
et l'inviter à nouveau serait un plan approprié. Un autre exemple serait si vous voulez lire une configuration d'un fichier et que ce fichier n'existe pas. Puisqu'il s'agit d'un fichier de configuration, vous pouvez avoir une configuration par défaut en tant que solution de secours, donc le fichier n'est pas exactement nécessaire. Donc, attraper un FileNotFoundError
et simplement appliquer la configuration par défaut serait un bon plan ici. Maintenant, dans ces deux cas, nous avons une exception très spécifique à laquelle nous nous attendons et avons un plan tout aussi spécifique pour s'en remettre. Ainsi, dans chaque cas, nous expliquons uniquement except
que certains exception.
Cependant, si nous devions tout attraper , alors - en plus des exceptions dont nous sommes prêts à nous remettre - il y a aussi une chance que nous obtenions des exceptions auxquelles nous ne nous attendions pas et que nous ne pouvons en effet pas récupérer; ou ne devrait pas récupérer.
Prenons l'exemple du fichier de configuration ci-dessus. En cas de fichier manquant, nous venons d'appliquer notre configuration par défaut, et nous pourrions décider ultérieurement de sauvegarder automatiquement la configuration (donc la prochaine fois, le fichier existera). Imaginez maintenant que nous obtenions un IsADirectoryError
ou unPermissionError
au lieu. Dans de tels cas, nous ne voulons probablement pas continuer; nous pourrions toujours appliquer notre configuration par défaut, mais nous ne pourrons plus enregistrer le fichier ultérieurement. Et il est probable que l'utilisateur voulait également avoir une configuration personnalisée, donc l'utilisation des valeurs par défaut n'est probablement pas souhaitée. Nous voudrions donc en informer immédiatement l'utilisateur, et probablement aussi interrompre l'exécution du programme. Mais ce n'est pas quelque chose que nous voulons faire quelque part au fond d'une petite partie de code; c'est quelque chose d'importance au niveau de l'application, donc cela devrait être géré en haut - alors laissez l'exception bouillonner.
Un autre exemple simple est également mentionné dans le document sur les idiomes Python 2 . Ici, une simple faute de frappe existe dans le code qui la fait casser. Parce que nous interceptons toutes les exceptions, nous interceptons également NameError
s et SyntaxError
s . Les deux sont des erreurs qui nous arrivent à tous lors de la programmation; et les deux sont des erreurs que nous ne voulons absolument pas inclure lors de l'expédition du code. Mais parce que nous les avons également capturés, nous ne saurons même pas qu'ils se sont produits là-bas et perdons toute aide pour le déboguer correctement.
Mais il existe également des exceptions plus dangereuses auxquelles nous ne sommes probablement pas préparés. Par exemple, SystemError est généralement quelque chose qui se produit rarement et que nous ne pouvons pas vraiment planifier; cela signifie qu'il se passe quelque chose de plus compliqué, quelque chose qui nous empêche probablement de poursuivre la tâche actuelle.
Dans tous les cas, il est très peu probable que vous soyez prêt à tout dans une petite partie du code, c'est donc vraiment là que vous ne devez intercepter que les exceptions pour lesquelles vous êtes préparé. Certaines personnes suggèrent au moins d'attraper Exception
car cela n'inclura pas des choses comme SystemExit
et KeyboardInterrupt
qui, par conception, doivent mettre fin à votre application, mais je dirais que c'est encore beaucoup trop peu spécifique. Il n'y a qu'un seul endroit où j'accepte personnellement la capture Exception
ou tout autreexception, et qui se trouve dans un seul gestionnaire d'exceptions global au niveau de l'application qui a pour seul but de consigner toute exception pour laquelle nous n'étions pas préparés. De cette façon, nous pouvons toujours conserver autant d'informations sur les exceptions inattendues, que nous pouvons ensuite utiliser pour étendre notre code pour les gérer explicitement (si nous pouvons les récupérer) ou - en cas de bogue - pour créer des cas de test pour nous assurer cela ne se reproduira plus. Mais bien sûr, cela ne fonctionne que si nous ne détectons que les exceptions que nous attendions déjà, donc celles auxquelles nous ne nous attendions pas vont naturellement bouillonner.
Essayez d'éviter de passer à l'exception des blocs
Lors de la capture explicite d'une petite sélection d'exceptions spécifiques, il existe de nombreuses situations dans lesquelles nous serons bien en ne faisant simplement rien. Dans de tels cas, le simple fait d'avoir except SomeSpecificException: pass
est très bien. La plupart du temps cependant, ce n'est pas le cas car nous avons probablement besoin d'un code lié au processus de récupération (comme mentionné ci-dessus). Cela peut être par exemple quelque chose qui recommence l'action, ou pour configurer une valeur par défaut à la place.
Si ce n'est pas le cas, par exemple parce que notre code est déjà structuré pour se répéter jusqu'à ce qu'il réussisse, alors le simple passage est suffisant. En prenant notre exemple ci-dessus, nous pourrions vouloir demander à l'utilisateur d'entrer un nombre. Parce que nous savons que les utilisateurs aiment ne pas faire ce que nous leur demandons, nous pourrions simplement le mettre en boucle en premier lieu, afin que cela ressemble à ceci:
def askForNumber ():
while True:
try:
return int(input('Please enter a number: '))
except ValueError:
pass
Parce que nous continuons d'essayer jusqu'à ce qu'aucune exception ne soit levée, nous n'avons pas besoin de faire quoi que ce soit de spécial dans le bloc except, donc c'est très bien. Mais bien sûr, on pourrait faire valoir que nous voulons au moins montrer à l'utilisateur un message d'erreur pour lui dire pourquoi il doit répéter l'entrée.
Dans de nombreux autres cas cependant, le simple fait de passer except
un signe indique que nous n'étions pas vraiment préparés à l'exception que nous attrapons. À moins que ces exceptions ne soient simples (comme ValueError
ou TypeError
) et que la raison pour laquelle nous pouvons passer soit évidente, essayez d'éviter simplement de passer. S'il n'y a vraiment rien à faire (et vous en êtes absolument sûr), pensez à ajouter un commentaire expliquant pourquoi c'est le cas; sinon, développez le bloc except pour inclure réellement du code de récupération.
except: pass
Le pire contrevenant est la combinaison des deux. Cela signifie que nous détectons volontiers toute erreur bien que nous ne soyons absolument pas préparés à cela et que nous ne fassions rien non plus. Vous souhaitez au moins enregistrer l'erreur et également la relancer pour terminer l'application (il est peu probable que vous puissiez continuer comme d'habitude après une MemoryError). Le simple fait de passer ne gardera pas seulement l'application un peu vivante (selon l'endroit où vous attrapez bien sûr), mais jettera également toutes les informations, ce qui rendra impossible de découvrir l'erreur - ce qui est particulièrement vrai si vous n'êtes pas celui qui la découvre.
Donc, l'essentiel est le suivant: n'attrapez que les exceptions auxquelles vous vous attendez vraiment et que vous êtes prêt à récupérer; tous les autres sont probablement soit des erreurs que vous devriez corriger, soit quelque chose auquel vous n'êtes pas préparé de toute façon. Passer des exceptions spécifiques est très bien si vous n'avez vraiment pas besoin de faire quelque chose à leur sujet. Dans tous les autres cas, c'est juste un signe de présomption et d'être paresseux. Et vous voulez vraiment résoudre ce problème.
logging
module au niveau DEBUG pour éviter leur diffusion en production, mais gardez-les disponibles en développement.