Je travaille sur le livre "Head First Python" (c'est ma langue à apprendre cette année) et je suis arrivé dans une section où ils discutent de deux techniques de code:
Checking First vs Exception traitant.
Voici un exemple du code Python:
# Checking First
for eachLine in open("../../data/sketch.txt"):
if eachLine.find(":") != -1:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
# Exception handling
for eachLine in open("../../data/sketch.txt"):
try:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
except:
pass
Le premier exemple traite directement d'un problème dans la .split
fonction. Le second laisse simplement le gestionnaire d'exceptions le gérer (et ignore le problème).
Ils expliquent dans le livre d'utiliser la gestion des exceptions au lieu de vérifier en premier. L'argument est que le code d'exception interceptera toutes les erreurs, alors que vérifier en premier ne capturera que les choses auxquelles vous pensez (et vous manquez les cas critiques). On m'a appris à vérifier d'abord, alors mon instinct de départ était de le faire, mais leur idée est intéressante. Je n'avais jamais pensé à utiliser le traitement des exceptions pour traiter les cas.
Lequel des deux est généralement considéré comme la meilleure pratique?
if -1 == eachLine.find(":"): continue
le reste de la boucle ne serait pas non plus en retrait.Réponses:
Dans .NET, il est courant d'éviter la surutilisation des exceptions. L'un des arguments est la performance: dans .NET, le lancement d'une exception est coûteux en calcul.
Une autre raison d'éviter leur utilisation abusive est qu'il peut être très difficile de lire du code qui repose trop sur eux. L' entrée de blog de Joel Spolsky décrit bien le problème.
Au cœur de l'argumentation se trouve la citation suivante:
Personnellement, je jette des exceptions lorsque mon code ne peut pas faire ce pour quoi il est sous contrat. J'ai tendance à utiliser try / catch lorsque je suis sur le point de traiter quelque chose en dehors de la limite de mon processus, par exemple un appel SOAP, un appel à une base de données, un fichier IO ou un appel système. Sinon, je tente de coder de manière défensive. Ce n'est pas une règle absolue, mais c'est une pratique générale.
Scott Hanselman écrit également sur les exceptions dans .NET ici . Dans cet article, il décrit plusieurs règles empiriques concernant les exceptions. Mon favori?
la source
En Python en particulier, il est généralement considéré comme une meilleure pratique d'attraper l'exception. Il a tendance à s'appeler plus facile à demander pardon que la permission (EAFP), comparé à Look Before You Leap (LBYL). Il y a des cas où LBYL vous donnera des bugs subtils dans certains cas.
Cependant, faites attention aux
except:
déclarations nues et aux déclarations trop générales, car elles peuvent toutes les deux masquer des bogues - une telle chose serait mieux:la source
try
/except
plutôt que la création d'une condition pour "est le suivant susceptible de lever une exception", et la pratique de Python est clairement de préférer l'ancienne. Cela ne fait pas de mal que cette approche soit (probablement) plus efficace ici, car, dans le cas où la scission est réussie, vous ne faites marcher la corde qu'une seule fois. Pour ce quisplit
est de savoir si une exception devrait être jetée ici, je dirais que c’est absolument le cas - une règle commune est que vous devriez jeter lorsque vous ne pouvez pas faire ce que dit votre nom et que vous ne pouvez pas vous séparer d’un séparateur manquant.Une approche pragmatique
Vous devriez être sur la défensive mais jusqu'à un certain point. Vous devriez écrire la gestion des exceptions mais jusqu'à un certain point. Je vais utiliser la programmation Web à titre d'exemple car c'est là que je vis.
Cela provient de l'expérience de travail dans des scénarios de grande équipe.
Une analogie
Imaginez si vous portiez une combinaison spatiale à l'intérieur de l'ISS TOUT le temps. Il serait difficile d'aller aux toilettes ou de manger, du tout. Ce serait super encombrant de se déplacer dans le module spatial. Ce serait nul. Écrire un tas d'essais dans votre code est un peu comme ça. Vous devez avoir un moment où vous dites, hé, j’ai sécurisé l’ISS et que mes astronautes à l’intérieur sont en bon état, il n’est donc pas pratique de porter une combinaison spatiale pour chaque scénario qui pourrait éventuellement se produire.
la source
L'argument principal du livre est que la version d'exception du code est préférable car elle intercepte tout ce que vous pourriez avoir oublié si vous tentiez d'écrire votre propre vérification d'erreur.
Je pense que cette affirmation n’est vraie que dans des circonstances très spécifiques - où vous ne vous souciez pas de savoir si le résultat est correct.
Il ne fait aucun doute que le fait de lever des exceptions est une pratique saine et sûre. Vous devriez le faire chaque fois que vous sentez qu'il y a quelque chose dans l'état actuel du programme que vous (en tant que développeur) ne pouvez pas, ou ne voulez pas, traiter.
Votre exemple, cependant, concerne la capture d’ exceptions. Si vous attrapez une exception, vous ne vous protégez pas des scénarios que vous avez peut-être négligés. Vous faites exactement le contraire: vous supposez que vous n’avez négligé aucun scénario qui aurait pu causer ce type d’exception et vous êtes donc confiant que vous pouvez l’attraper (et l’empêcher ainsi de provoquer la fermeture du programme, comme le ferait toute exception non interceptée).
En utilisant l'approche d'exception, si vous voyez une
ValueError
exception, vous sautez une ligne. En utilisant l'approche traditionnelle sans exception, vous comptez le nombre de valeurs renvoyéessplit
et, s'il est inférieur à 2, vous sautez une ligne. Devriez-vous vous sentir plus en sécurité avec l'approche d'exception, puisque vous auriez peut-être oublié d'autres situations "d'erreur" dans votre vérification d'erreur classique et que vousexcept ValueError
voudriez les résoudre?Cela dépend de la nature de votre programme.
Si vous écrivez, par exemple, dans un navigateur Web ou un lecteur vidéo, un problème d’entrées ne devrait pas provoquer sa panne avec une exception non interceptée. Il est de loin préférable de sortir quelque chose de sensible (même si, à proprement parler, d’erreur) que de quitter.
Si vous écrivez une application dans laquelle l'exactitude est importante (comme un logiciel commercial ou d'ingénierie), ce serait une approche terrible. Si vous avez oublié certains scénarios
ValueError
, la pire chose à faire est d'ignorer en silence ce scénario inconnu et de simplement sauter la ligne. C'est ainsi que des bogues très subtils et coûteux se retrouvent dans les logiciels.Vous pourriez penser que la seule façon de voir
ValueError
ce code est desplit
renvoyer une seule valeur (au lieu de deux). Mais que se passe-t-il si, par la suite, votreprint
déclaration commence à utiliser une expression qui soulèveValueError
certaines conditions? Cela vous fera sauter certaines lignes non pas parce qu'elles manquent:
, mais parceprint
qu'elles échouent. Voici un exemple de bogue subtil auquel je faisais référence précédemment: vous ne remarqueriez rien, vous perdez simplement quelques lignes.Ma recommandation est d'éviter d'attraper (mais pas de lever!) Des exceptions dans le code où produire une sortie incorrecte est pire que sortir. La seule fois où j'attrape une exception dans un tel code, c'est lorsque j'ai une expression vraiment triviale. Je peux donc facilement déterminer la cause de chacun des types d'exception possibles.
En ce qui concerne l'impact sur les performances de l'utilisation des exceptions, il est trivial (en Python), à moins que des exceptions ne soient rencontrées fréquemment.
Si vous utilisez des exceptions pour gérer des conditions courantes, vous pouvez parfois payer un coût de performances énorme. Par exemple, supposons que vous exécutiez une commande à distance. Vous pouvez vérifier que le texte de votre commande passe au moins la validation minimale (par exemple, la syntaxe). Vous pouvez également attendre qu'une exception soit déclenchée (ce qui ne se produit qu'une fois que le serveur distant a analysé votre commande et a constaté un problème). De toute évidence, le premier est beaucoup plus rapide. Autre exemple simple: vous pouvez vérifier si un nombre est égal à zéro ~ 10 fois plus rapidement que d'essayer d'exécuter la division puis d'attraper l'exception ZeroDivisionError.
Ces considérations importent uniquement si vous envoyez fréquemment des chaînes de commandes mal formées à des serveurs distants ou si vous recevez des arguments de valeur zéro que vous utilisez pour la division.
Note: Je suppose que vous utiliseriez à la
except ValueError
place du justeexcept
; comme d'autres l'ont fait remarquer, et comme le livre lui-même l'indique en quelques pages, il ne faut jamais utiliser nuexcept
.Autre remarque: l'approche appropriée sans exception consiste à compter le nombre de valeurs renvoyées par
split
plutôt qu'à la recherche:
. Ce dernier est beaucoup trop lent, car il répète le travail effectuésplit
et peut presque doubler le temps d'exécution.la source
En règle générale, si vous savez qu'une instruction peut générer un résultat non valide, testez-la et corrigez-la. Utilisez des exceptions pour des choses que vous n'attendez pas; des trucs qui sont "exceptionnels". Cela rend le code plus clair dans un sens contractuel ("ne devrait pas être nul" à titre d'exemple).
la source
Utilisez ce qui marche bien dans ..
La gestion des exceptions et la programmation défensive sont des manières différentes d'exprimer la même intention.
la source
TBH, peu importe que vous utilisiez le
try/except
mécanicien ou unif
contrôle de relevé. Vous voyez généralement à la fois EAFP et LBYL dans la plupart des lignes de base Python, EAFP étant légèrement plus commun. Parfois, EAFP est beaucoup plus lisible / idiomatique, mais dans ce cas particulier, je pense que ça va de toute façon.Pourtant...
Je ferais attention en utilisant votre référence actuelle. Quelques problèmes criants avec leur code:
with
idiome lors de la lecture de fichiers en Python: il y a très peu d'exceptions. Ce n'est pas l'un d'eux.except
instructions nues qui ne détectent aucune exception spécifique)role, lineSpoken = eachLine.split(":",1)
Ivc a une bonne réponse à ce sujet et à l'EAFP, mais fait également fuir le descripteur.
La version LBYL n'étant pas nécessairement aussi performante que la version EAFP, affirmer que le lancement d'exceptions est "coûteux en termes de performances" est catégoriquement faux. Cela dépend vraiment du type de chaîne que vous traitez:
la source
Fondamentalement, la gestion des exceptions est censée être plus appropriée pour les langues POO.
Le deuxième point est la performance, car vous n'avez pas à exécuter
eachLine.find
pour chaque ligne.la source
Je pense que la programmation défensive nuit aux performances. Vous devez également ne capturer que les exceptions que vous allez gérer, laissez le runtime s'occuper de l'exception que vous ne savez pas gérer.
la source