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:

Vérifier membre de l'existence en Python

96
demandé sur Community 2009-12-02 23:58:37

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 et except . Le la technique contraste avec le LBYL style commun à beaucoup d'autres langues comme C.

170
répondu Tim Pietzcker 2017-07-11 17:49:26

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
11
répondu Brandon 2009-12-02 21:11:16

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.

9
répondu tzot 2009-12-02 21:57:37

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.

4
répondu Dave Kirby 2009-12-02 21:07:08

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.)

3
répondu bobince 2009-12-02 22:39:14

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
3
répondu Managu 2009-12-03 00:43:07

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.

2
répondu AFoglia 2017-05-23 12:02:38

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.

0
répondu grayger 2009-12-03 02:33:22

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.

-4
répondu ilowe 2009-12-02 21:12:45