Dois-je toujours spécifier un type d'exception dans les instructions «except»?

93

Lorsque vous utilisez PyCharm IDE, l'utilisation de except:sans type d'exception déclenche un rappel de l'EDI indiquant que cette clause d'exception est Too broad.

Dois-je ignorer ce conseil? Ou est-ce pythonique de toujours spécifier le type d'exception?

HorseloverGraisse
la source
Si vous voulez en savoir plus à ce sujet que les réponses, google avale les exceptions. Vous pouvez toutes sortes d'autres choses intéressantes à faire et à ne pas faire à côté d'eux. Le code sent en est un autre.
Tony Hopkinson

Réponses:

89

Il est presque toujours préférable de spécifier un type d'exception explicite. Si vous utilisez une except:clause nue , vous pourriez finir par attraper des exceptions autres que celles que vous prévoyez d'attraper - cela peut masquer des bogues ou rendre plus difficile le débogage des programmes lorsqu'ils ne font pas ce que vous attendez.

Par exemple, si vous insérez une ligne dans une base de données, vous souhaiterez peut-être intercepter une exception indiquant que la ligne existe déjà, afin de pouvoir effectuer une mise à jour.

try:
    insert(connection, data)
except:
    update(connection, data)

Si vous spécifiez un bare except:, vous attraperez également une erreur de socket indiquant que le serveur de base de données est tombé. Il est préférable de n'attraper que les exceptions que vous savez gérer - il est souvent préférable que le programme échoue au point de l'exception que de continuer mais se comporte de manière étrange et inattendue.

Un cas où vous voudrez peut-être utiliser un bare except:est au niveau supérieur d'un programme dont vous devez toujours être en cours d'exécution, comme un serveur réseau. Mais alors, vous devez faire très attention à enregistrer les exceptions, sinon il sera impossible de déterminer ce qui ne va pas. Fondamentalement, il ne devrait y avoir au plus qu'un seul endroit dans un programme qui fait cela.

Un corollaire à tout cela est que votre code ne devrait jamais faire raise Exception('some message')car il oblige le code client à utiliser except:(ou except Exception:qui est presque aussi mauvais). Vous devez définir une exception spécifique au problème que vous souhaitez signaler (peut-être héritant d'une sous-classe d'exceptions intégrée comme ValueErrorou TypeError). Ou vous devez lever une exception intégrée spécifique. Cela permet aux utilisateurs de votre code d'être prudents en n'attrapant que les exceptions qu'ils souhaitent gérer.

babbageclunk
la source
7
+1 Très vrai. Encore plus amusant avec l'exemple: except:attrape également (parmi beaucoup d'autres choses) NameErroret AttributeError, donc, si vous avez mal orthographié quelque chose dans le trybloc (par exemple, votre fonction "insert" est en fait appelée insert_oneparce que quelqu'un n'a pas accordé autant d'importance à la cohérence qu'il le devrait), il essaie toujours silencieusement update().
1
Alors, qu'en est-il du moment où vous devez vous assurer qu'une exception ne sera pas lancée au-dessus du site d'appel actuel? C'est-à-dire - j'ai détecté toutes les exceptions spécifiques que je peux prévoir d'être levées, maintenant je dois ajouter que "si cette chose à laquelle je n'ai pas pensé est lancée, je dois la journaliser avant qu'elle ne tue le contexte d'exécution en cours" (comme main())?
Adam Parkin
C'est le genre de situation dont je parle dans le paragraphe commençant par «Un cas ...». C'est vraiment nécessaire parfois, mais tout programme donné ne devrait vraiment avoir qu'un seul endroit qui le fasse. Et vous devez faire attention à ce que cela indique clairement dans le journal ce qui se passe, sinon vous aurez un temps frustrant à essayer de comprendre pourquoi votre programme ne gère pas correctement l'événement / la demande / quoi que ce soit.
babbageclunk
3
@delnan: Pire que ça. except Exception:attrapera NameErroret AttributeErroraussi. Ce qui rend nu except:si mauvais, c'est qu'il attrape des trucs qui n'ont rien à faire, par exemple SystemExit(soulevé lorsque vous appelez exitou sys.exit, et maintenant vous avez empêché une sortie prévue) et KeyboardInterrupt(encore une fois, si l'utilisateur frappe Ctrl-C, vous ne voulez probablement pas continuer à courir juste pour les contrarier). Seul ce dernier a vraiment un sens à attraper, et il devrait être pris explicitement. Au moins except Exception:laisse ces deux se propager normalement.
ShadowRanger
38

Vous ne devez pas ignorer les conseils que vous donne l'interprète.

À partir du guide de style PEP-8 pour Python:

Lorsque vous interceptez des exceptions, mentionnez des exceptions spécifiques autant que possible au lieu d'utiliser une clause except: nue.

Par exemple, utilisez:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

Une clause except: nue interceptera les exceptions SystemExit et KeyboardInterrupt, rendant plus difficile l'interruption d'un programme avec Control-C, et peut masquer d'autres problèmes. Si vous voulez intercepter toutes les exceptions qui signalent des erreurs de programme, utilisez except Exception: (bare except équivaut à except BaseException :).

Une bonne règle d'or est de limiter l'utilisation de clauses d'exception nues à deux cas:

Si le gestionnaire d'exceptions imprimera ou enregistrera le traçage; au moins, l'utilisateur sera conscient qu'une erreur s'est produite. Si le code doit faire un travail de nettoyage, mais laisse ensuite l'exception se propager vers le haut avec augmentation. essayez ... enfin peut être une meilleure façon de gérer cette affaire.

asheeshr
la source
9

Pas spécifique à Python ceci.

Le but des exceptions est de traiter le problème aussi près que possible de son origine.

Vous gardez donc le code qui pourrait dans des circonstances exceptionnelles déclencher le problème et la résolution "à côté" l'un de l'autre.

Le fait est que vous ne pouvez pas connaître toutes les exceptions qui pourraient être levées par un morceau de code. Tout ce que vous pouvez savoir, c'est que s'il s'agit d'une exception, par exemple un fichier non trouvé, vous pouvez la piéger et inviter l'utilisateur à en obtenir une qui exécute ou annuler la fonction.

Si vous mettez try catch round that, quel que soit le problème qu'il y avait dans votre routine de fichiers (lecture seule, autorisations, UAC, pas vraiment un pdf, etc.), tout le monde tombera dans votre fichier non trouvé catch, et votre utilisateur crie "mais il est là, ce code est de la merde"

Maintenant, il y a quelques situations où vous pourriez tout attraper, mais elles devraient être choisies consciemment.

Ils sont catch, annulez une action locale (comme créer ou verrouiller une ressource, (ouvrir un fichier sur le disque pour écrire par exemple), puis vous relancez l'exception, à traiter à un niveau supérieur)

L'autre vous, c'est que vous ne vous souciez pas de la raison pour laquelle cela a mal tourné. L'impression par exemple. Vous pourriez avoir un hic, pour dire qu'il y a un problème avec votre imprimante, veuillez le régler et ne pas tuer l'application à cause de cela. Si votre code exécutait une série de tâches séparées en utilisant une sorte de calendrier, vous ne voudriez pas que le tout meure, car l'une des tâches a échoué.

Remarque Si vous faites ce qui précède, je ne peux pas recommander une sorte de journalisation des exceptions, par exemple essayez la fin du journal de capture, assez fortement.

Tony Hopkinson
la source
Je dirais que c'est une question d'équilibre. Vous devez attraper l'exception suffisamment tôt pour pouvoir s'en remettre et assez tard pour savoir où aller avec. C'est pourquoi la gestion des exceptions Java cause de tels ravages, car vous devez reconditionner l'exception à chaque étape et vous perdez des informations.
dhill
2
+1 pour "vous ne vous souciez pas de savoir pourquoi cela s'est mal passé" Je l'utilise à plusieurs endroits autour d'une seule ligne de code où j'analyse une date / heure à partir d'une URL. La bibliothèque d'analyse de date / heure tierce ne répertorie pas toutes les exceptions qu'elle peut lever (j'ai trouvé OverflowError et TypeError en plus du ValueError standard, mais il y en a probablement plus), et de toute façon je ne me soucie vraiment pas pourquoi une exception a été levée, je veux juste fournir un message d'erreur raisonnable à l'utilisateur en disant que quelque chose ne va pas avec la date / heure.
Michael Rodby
3

Vous attraperez également par exemple Control-C avec cela, alors ne le faites pas à moins que vous ne le "jetiez" à nouveau. Cependant, dans ce cas, vous devriez plutôt utiliser "enfin".

Ulrich Eckhardt
la source
3

Toujours préciser le type d'exception, il existe de nombreux types que vous ne voulez pas attraper, comme SyntaxError, KeyboardInterrupt, MemoryErroretc.

Pavel Anossov
la source
2
utiliserait except Exception:éviter les types ci-dessus que nous ne voulons pas attraper?
HorseloverFat
except Exceptionc'est bien.
Ulrich Eckhardt
4
@HorseloverFat: except Exceptionattrape SyntaxErroret MemoryErrorparce que c'est leur classe de base. KeyboardInterrupt, SystemExit(soulevées par sys.exit()) ne sont pas interceptées (ce sont des sous-classes immédiates de BaseException)
jfs
semble alors que ce n'est pas idéal - mieux vaut spécifier plus précisément.
HorseloverFat
3

Voici les endroits où j'utilise sauf sans type

  1. prototypage rapide et sale

C'est la principale utilisation dans mon code pour les exceptions non vérifiées

  1. fonction main () de niveau supérieur, où je consigne chaque exception non interceptée

J'ajoute toujours ceci, pour que le code de production ne déborde pas de stacktraces

  1. entre les couches d'application

J'ai deux façons de le faire:

  • Première façon de le faire: lorsqu'une couche de niveau supérieur appelle une fonction de niveau inférieur, elle encapsule les appels dans des excepts typés pour gérer les exceptions de niveau inférieur "supérieures". Mais j'ajoute une instruction except générique, pour détecter les exceptions de niveau inférieur non gérées dans les fonctions de niveau inférieur.

Je préfère cela, je trouve plus facile de détecter quelles exceptions auraient dû être capturées correctement: je "vois" mieux le problème lorsqu'une exception de niveau inférieur est enregistrée par un niveau supérieur

  • Deuxième façon de le faire: chaque fonction de niveau supérieur des couches de niveau inférieur ont leur code enveloppé dans un générique, sauf qu'il capture toutes les exceptions non gérées sur cette couche spécifique.

Certains collègues préfèrent cette méthode, car elle conserve les exceptions de niveau inférieur dans les fonctions de niveau inférieur, auxquelles elles «appartiennent».

nipil
la source
-14

Essaye ça:

try:
    #code
except ValueError:
    pass

J'ai obtenu la réponse de ce lien, si quelqu'un d'autre rencontre ce problème Vérifiez-le

Mekanic
la source