Je vois souvent des commentaires sur d'autres questions Stack Overflow sur la façon dont l'utilisation de except: pass
est découragée. Pourquoi est-ce mauvais? Parfois, je me fiche des erreurs et je veux simplement continuer avec le code.
try:
something
except:
pass
Pourquoi est-ce except: pass
mauvais d' utiliser un bloc? Qu'est-ce qui le rend mauvais? Est-ce le fait que je suis pass
sur une erreur ou que j'ai except
une erreur?
logging
module au niveau DEBUG pour éviter leur diffusion en production, mais gardez-les disponibles en développement.Réponses:
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:
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 unValueError
. Vous pouvez facilement récupérer cela en demandant simplement à l'utilisateur de réessayer, donc attraper leValueError
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 unFileNotFoundError
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 uniquementexcept
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 etSyntaxError
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 commeSystemExit
etKeyboardInterrupt
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 captureException
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:
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 (commeValueError
ouTypeError
) 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.
la source
except
, mais appellent ensuiteraise
sans argument pour continuer de laisser l'exception bouillonner, mettant fin à l'application. Je l'aime: ianbicking.org/blog/2007/09/re-raising-exceptions.html . Ressemble à une exception solide à la règle de ne pas utiliser la couvertureexcept
.raise
. Cependant, vous ne feriez généralement cela que dans quelques emplacements de votre application pour enregistrer l'exception.Le principal problème ici est qu'il ignore toutes les erreurs: Mémoire insuffisante, le processeur brûle, l'utilisateur veut s'arrêter, le programme veut quitter, Jabberwocky tue les utilisateurs.
C'est beaucoup trop. Dans votre tête, vous pensez "je veux ignorer cette erreur de réseau". Si quelque chose d' inattendu se passe mal, alors votre code continue silencieusement et se brise de manière complètement imprévisible que personne ne peut déboguer.
C'est pourquoi vous devez vous limiter à ignorer spécifiquement seulement certaines erreurs et laisser le reste passer.
la source
L'exécution de votre pseudo-code ne donne littéralement aucune erreur:
comme s'il s'agissait d'un morceau de code parfaitement valide, au lieu de lancer a
NameError
. J'espère que ce n'est pas ce que tu veux.la source
Cela intercepte toutes les exceptions possibles, y compris
GeneratorExit
,KeyboardInterrupt
etSystemExit
- qui sont des exceptions que vous n'avez probablement pas l'intention d'attraper. C'est la même chose que d'attraperBaseException
.Les anciennes versions de la documentation disent :
Hiérarchie des exceptions Python
Si vous interceptez une classe d'exception parent, vous interceptez également toutes leurs classes enfants. Il est beaucoup plus élégant de ne détecter que les exceptions que vous êtes prêt à gérer.
Voici la hiérarchie des exceptions Python 3 - voulez-vous vraiment les attraper tous ?:
Ne faites pas ça
Si vous utilisez cette forme de gestion des exceptions:
Vous ne pourrez alors pas interrompre votre
something
bloc avec Ctrl-C. Votre programme ignorera toutes les exceptions possibles à l'intérieur dutry
bloc de code.Voici un autre exemple qui aura le même comportement indésirable:
Au lieu de cela, essayez de ne saisir que l'exception spécifique que vous savez que vous recherchez. Par exemple, si vous savez que vous pourriez obtenir une erreur de valeur lors d'une conversion:
Explication supplémentaire avec un autre exemple
Vous le faites peut-être parce que vous êtes en train de gratter le Web et que
UnicodeError
, mais parce que vous avez utilisé la capture d'exception la plus large, votre code, qui peut avoir d'autres défauts fondamentaux, tentera de s'exécuter complètement, gaspillant la bande passante , temps de traitement, usure de votre équipement, manque de mémoire, collecte de données sur les déchets, etc.Si d'autres personnes vous demandent de terminer pour pouvoir compter sur votre code, je comprends que je me sens obligé de tout gérer. Mais si vous êtes prêt à échouer bruyamment à mesure que vous vous développez, vous aurez la possibilité de corriger des problèmes qui ne pourraient apparaître que de manière intermittente, mais ce seraient des bogues coûteux à long terme.
Avec une gestion des erreurs plus précise, votre code peut être plus robuste.
la source
Alors, voici mon avis. Chaque fois que vous trouvez une erreur, vous devez faire quelque chose pour la gérer, c'est-à-dire l'écrire dans le fichier journal ou autre chose. Au moins, il vous informe qu'il y avait une erreur.
la source
Vous devez utiliser au moins
except Exception:
pour éviter d'attraper des exceptions système commeSystemExit
ouKeyboardInterrupt
. Voici le lien vers les documents.En général, vous devez définir explicitement les exceptions que vous souhaitez intercepter, pour éviter d'attraper des exceptions indésirables. Vous devez savoir quelles exceptions vous ignorez .
la source
Tout d'abord, il viole deux principes du Zen of Python :
Cela signifie que vous faites intentionnellement passer votre erreur en silence. De plus, vous ne savez pas quelle erreur s'est exactement produite, car
except: pass
elle interceptera toute exception.Deuxièmement, si nous essayons de nous éloigner du Zen de Python et de parler en termes de juste santé mentale, vous devez savoir que l'utilisation
except:pass
vous laisse sans connaissance ni contrôle dans votre système. La règle d'or consiste à lever une exception, en cas d'erreur, et à prendre les mesures appropriées. Si vous ne savez pas à l'avance quelles actions celles-ci devraient être, enregistrez au moins l'erreur quelque part (et mieux relancez l'exception):Mais, généralement, si vous essayez d'attraper une exception, vous faites probablement quelque chose de mal!
la source
La
except:pass
construction fait essentiellement taire toutes les conditions exceptionnelles qui surviennent pendant l'try:
exécution du code couvert par le bloc.Ce qui rend cette mauvaise pratique, c'est que ce n'est généralement pas ce que vous voulez vraiment. Le plus souvent, une condition spécifique apparaît que vous voulez faire taire, et
except:pass
c'est trop un instrument contondant. Il fera le travail, mais il masquera également d'autres conditions d'erreur que vous n'avez probablement pas prévues, mais que vous voudrez peut-être traiter d'une autre manière.Ce qui rend cela particulièrement important en Python, c'est que par les idiomes de ce langage, les exceptions ne sont pas nécessairement des erreurs . Ils sont souvent utilisés de cette façon, bien sûr, comme dans la plupart des langues. Mais Python en particulier les a parfois utilisés pour implémenter un chemin de sortie alternatif à partir de certaines tâches de code qui ne fait pas vraiment partie du cas normal, mais qui est toujours connu pour apparaître de temps en temps et peut même être attendu dans la plupart des cas.
SystemExit
a déjà été mentionné comme un ancien exemple, mais l'exemple le plus courant de nos jours peut êtreStopIteration
. L'utilisation d'exceptions de cette façon a provoqué beaucoup de controverse, en particulier lorsque les itérateurs et les générateurs ont été introduits pour la première fois dans Python, mais finalement l'idée a prévalu.la source
La raison n ° 1 a déjà été indiquée - elle cache des erreurs auxquelles vous ne vous attendiez pas.
(# 2) - Cela rend votre code difficile à lire et à comprendre pour les autres. Si vous interceptez une FileNotFoundException lorsque vous essayez de lire un fichier, il est assez évident pour un autre développeur de savoir quelles fonctionnalités le bloc "catch" devrait avoir. Si vous ne spécifiez pas d'exception, vous avez besoin de commentaires supplémentaires pour expliquer ce que le bloc doit faire.
(# 3) - Il montre une programmation paresseuse. Si vous utilisez le try / catch générique, cela indique soit que vous ne comprenez pas les erreurs d'exécution possibles dans votre programme, soit que vous ne savez pas quelles exceptions sont possibles en Python. La détection d'une erreur spécifique montre que vous comprenez à la fois votre programme et la plage d'erreurs que Python lance. Il est plus probable que d'autres développeurs et réviseurs de code fassent confiance à votre travail.
la source
Alors, quelle sortie ce code produit-il?
Imaginez maintenant que le bloc
try
-except
soit des centaines de lignes d'appels vers une hiérarchie d'objets complexe, et soit lui-même appelé au milieu de l'arborescence des appels d'un grand programme. Lorsque le programme tourne mal, où commencez-vous à chercher?la source
En général, vous pouvez classer toute erreur / exception dans l'une des trois catégories suivantes :
Fatal : Ce n'est pas de votre faute, vous ne pouvez pas les empêcher, vous ne pouvez pas vous en remettre. Vous ne devez certainement pas les ignorer et continuer, et laisser votre programme dans un état inconnu. Laissez simplement l'erreur terminer votre programme, vous ne pouvez rien faire.
Boneheaded : Votre propre faute, probablement due à une erreur de surveillance, de bogue ou de programmation. Vous devez corriger le bogue. Encore une fois, vous ne devez certainement pas ignorer et continuer.
Exogène : vous pouvez vous attendre à ces erreurs dans des situations exceptionnelles, comme un fichier introuvable ou une connexion interrompue . Vous devez explicitement gérer ces erreurs, et uniquement celles-ci.
Dans tous les cas
except: pass
, votre programme ne restera que dans un état inconnu, où il peut causer plus de dégâts.la source
Autrement dit, si une exception ou une erreur est levée, quelque chose ne va pas. Ce n'est peut-être pas quelque chose de très mal, mais créer, lancer et intercepter des erreurs et des exceptions juste pour le plaisir d'utiliser des instructions goto n'est pas une bonne idée, et c'est rarement fait. 99% du temps, il y avait un problème quelque part.
Les problèmes doivent être traités. Tout comme c'est le cas dans la vie, dans la programmation, si vous laissez les problèmes tranquilles et essayez de les ignorer, ils ne disparaissent pas souvent d'eux-mêmes; au lieu de cela, ils grossissent et se multiplient. Pour éviter qu'un problème ne se développe sur vous et ne frappe à nouveau plus loin sur la route, vous 1) l'éliminez et nettoyez le désordre par la suite, ou 2) le contenez et nettoyez le désordre par la suite.
Ignorer les exceptions et les erreurs et les laisser comme cela est un bon moyen de subir des fuites de mémoire, des connexions de base de données exceptionnelles, des verrous inutiles sur les autorisations de fichiers, etc.
En de rares occasions, le problème est si minuscule, trivial et - en plus d'avoir besoin d'un bloc try ... catch - autonome , qu'il n'y a vraiment aucun gâchis à nettoyer par la suite. Ce sont les seules occasions où cette meilleure pratique ne s'applique pas nécessairement. D'après mon expérience, cela signifie généralement que tout ce que le code fait est fondamentalement mesquin et évitable, et quelque chose comme les tentatives de nouvelle tentative ou les messages spéciaux ne valent ni la complexité ni le maintien du fil.
Dans mon entreprise, la règle est de faire presque toujours quelque chose dans un bloc catch, et si vous ne faites rien, vous devez toujours placer un commentaire avec une très bonne raison. Vous ne devez jamais passer ou laisser un bloc d'arrêt vide quand il y a quelque chose à faire.
la source
À mon avis, les erreurs ont une raison d'apparaître, que mon son est stupide, mais c'est ainsi. Une bonne programmation ne soulève des erreurs que lorsque vous devez les gérer. Aussi, comme je l'ai lu il y a quelque temps, "la pass-Statement est une déclaration qui montre que le code sera inséré plus tard", donc si vous voulez avoir une instruction except vide, n'hésitez pas à le faire, mais pour un bon programme, il y aura être une partie manquante. parce que vous ne gérez pas les choses que vous devriez avoir. Les exceptions qui apparaissent vous permettent de corriger les données d'entrée ou de modifier la structure de vos données afin que ces exceptions ne se reproduisent pas (mais dans la plupart des cas (exceptions réseau, exceptions générales d'entrée), les exceptions indiquent que les parties suivantes du programme ne s'exécuteront pas correctement. Par exemple, une NetworkException peut indiquer une connexion réseau interrompue et le programme ne peut pas envoyer / recevoir de données dans les prochaines étapes du programme.
Mais l'utilisation d'un bloc de passe pour un seul bloc d'exception est valide, car vous différenciez toujours les types d'exceptions, donc si vous mettez tous les blocs d'exception en un, il n'est pas vide:
peut être réécrit de cette façon:
Ainsi, même plusieurs blocs except avec des instructions de passage peuvent entraîner du code, dont la structure gère des types spéciaux d'exceptions.
la source
Tous les commentaires soulevés jusqu'à présent sont valables. Dans la mesure du possible, vous devez spécifier exactement quelle exception vous souhaitez ignorer. Dans la mesure du possible, vous devez analyser la cause de l'exception et ignorer uniquement ce que vous vouliez ignorer, et non le reste. Si une exception provoque un "crash spectaculaire" de l'application, alors qu'il en soit ainsi, car il est beaucoup plus important de savoir que l'inattendu s'est produit lorsqu'il s'est produit, que de cacher que le problème s'est jamais produit.
Cela dit, ne prenez aucune pratique de programmation comme une priorité. C'est stupide. Il y a toujours le temps et le lieu pour faire ignorer toutes les exceptions.
Un autre exemple de principe idiot est l'utilisation de l'
goto
opérateur. Quand j'étais à l'école, notre professeur nous a apprisgoto
opérateur juste pour mentionner que tu ne l'utiliseras jamais. Ne croyez pas que les gens vous disent que xyz ne devrait jamais être utilisé et qu'il ne peut y avoir de scénario quand il est utile. Il y en a toujours.la source
La gestion des erreurs est très importante dans la programmation. Vous devez montrer à l'utilisateur ce qui ne va pas. Dans très peu de cas, vous pouvez ignorer les erreurs. C'est une très mauvaise pratique de programmation.
la source
Comme il n'a pas encore été mentionné, il est préférable d'utiliser un style
contextlib.suppress
:Notez que dans l'exemple fourni, l'état du programme reste le même, que l'exception se produise ou non. C'est-à-dire,
somefile.tmp
devient toujours inexistant.la source