Utilisation de try vs if en python
y a-t-il une justification pour décider lequel des try
ou if
construit à utiliser, lors de l'essai de variable pour avoir une valeur?
par exemple, il y a une fonction qui renvoie une liste ou qui ne renvoie pas de valeur. Je veux vérifier les résultats avant de les traiter. Lequel des énoncés suivants serait plus préférable et pourquoi?
result = function();
if (result):
for r in result:
#process items
ou
result = function();
try:
for r in result:
#process items
except TypeError:
pass;
la discussion à ce sujet:
9 réponses
on entend souvent dire que Python encourage l'aeap "1519160920 de style" ("il est plus facile de demander pardon que demander la permission") sur LBYL style"avant de faire le saut"). Pour moi, c'est une question d'efficacité et de lisibilité.
dans votre exemple (dire qu'au lieu de retourner une liste ou une chaîne vide, la fonction devait retourner une liste ou None
), si vous vous attendez à ce que 99% du temps result
contienne réellement quelque chose d'itérable, j'utiliserais l'approche try/except
. Ce sera plus rapide si les exceptions sont vraiment exceptionnelles. Si result
est None
plus de 50% du temps, alors utiliser if
est probablement mieux.
pour supporter ceci avec quelques mesures:
>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
0.051202772912802175
, Alors que if
déclaration toujours vous en coûte, c'est presque un try/except
bloquer. Mais quand un Exception
en fait se produit, le coût est beaucoup plus élevé.
Morale:
- Il est parfaitement OK (et "pythonic") d'utilisation de
try/except
pour le contrôle de flux, - mais c'est le plus logique quand
Exception
s sont réellement exceptionnel.
De l'Python docs:
l'aeap
Plus facile de demander pardon que autorisation. Ce code Python commun le style suppose l'existence de clés ou attributs et captures exceptions si l'hypothèse s'avère faux. Ce style propre et rapide est caractérisé par la présence de nombreux
try
etexcept
. Le la technique contraste avec le LBYL style commun à beaucoup d'autres langues comme C.
votre fonction ne doit pas retourner les types mixtes (c.-à-d. liste ou chaîne vide). Il devrait retourner une liste de valeurs ou juste une liste vide. Alors vous n'auriez pas besoin de tester quoi que ce soit, c.-à-d. votre code s'effondre en:
for r in function():
# process items
s'il vous Plaît ignorer ma solution si le code que j'ai n'est pas évident au premier coup d'œil et vous avez qu'à lire l'explication après l'exemple de code.
puis-je supposer que" Aucune valeur retournée " signifie Aucune valeur retournée? Si oui, ou si" no value "est False boolean-wise, vous pouvez faire ce qui suit, puisque votre code traite essentiellement "no value" comme "ne pas itérer":
for r in function() or ():
# process items
si function()
retourne quelque chose qui n'est pas vrai, vous itérer sur le tuple vide, c'est à dire que vous ne courrez aucun itérations. C'est essentiellement de la LBYL.
votre deuxième exemple est cassé - le code ne jettera jamais une exception TypeError puisque vous pouvez itérer à la fois les chaînes et les listes. Itérer par une chaîne ou une liste vide est également valide - il exécutera le corps de la boucle zéro fois.
Lequel des éléments suivants serait plus préférable et pourquoi?
regardez avant de sauter est préférable dans ce cas. Avec l'approche d'exception, une erreur typographique pourrait se produire n'importe où dans votre corps de boucle et elle se ferait attraper et jeter, ce qui n'est pas ce que vous voulez et rendra le débogage difficile.
(je suis d'accord avec Brandon Corfman: retour Aucun des "objets" au lieu d'une liste vide est cassé. C'est un mauvaise habitude des codeurs Java qu'on ne devrait pas voir en Python. Ou Java.)
en général, j'ai l'impression que les exceptions devraient être réservées aux circonstances exceptionnelles. Si l'on s'attend à ce que le result
ne soit jamais vide (mais pourrait l'être si, par exemple, un disque s'est écrasé, etc.), la deuxième approche a du sens. Si, par contre, un result
vide est parfaitement raisonnable dans des conditions normales, le tester avec un if
est plus logique.
j'avais à l'esprit le scénario (plus courant) :
# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
file_counts[filename]=0
file_counts[filename]+=1
au lieu de l'équivalent:
...
try:
file_counts[filename]+=1
except KeyError:
file_counts[filename]=1
bobince fait sagement remarquer que le fait d'envelopper le second boîtier peut également attraper des erreurs typographiques dans la boucle, ce qui n'est pas ce que vous voulez. Si vous ne voulez vraiment utiliser un essai, vous pouvez tester si c'est itérable avant la boucle
result = function();
try:
it = iter(result)
except TypeError:
pass
else:
for r in it:
#process items
Comme vous pouvez le voir, c'est assez moche. Je ne le suggère pas, mais il faut le mentionner pour qu'il soit complet.
en ce qui concerne la performance, en utilisant le bloc d'essai pour le code qui normalement doesn't raise exceptions est plus rapide que l'utilisation de l'instruction if à chaque fois. Donc, la décision dépend de la probabilité de cas exceptionnels.
comme une règle générale de pouce, vous devriez jamais utiliser try/catch ou toute autre chose de manipulation d'exception pour contrôler le flux. Même si l'itération dans les coulisses est contrôlée via la levée des exceptions StopIteration
, vous devriez quand même préférer votre premier fragment de code à la seconde.