Est-ce une bonne pratique d'utiliser try-except-else en Python?

de temps en temps en Python, je vois le bloc:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

Quelle est la raison de l'essai-sauf-pour exister?

Je n'aime pas ce genre de programmation, car elle utilise des exceptions pour effectuer le contrôle du débit. Cependant, s'il est inclus dans la langue, il doit y avoir une bonne raison à cela, n'est-ce pas?

je crois comprendre que les exceptions ne sont pas des erreurs , et qu'ils ne devraient être utilisés que dans des conditions exceptionnelles (par exemple, j'essaie d'écrire un fichier dans un disque et il n'y a plus d'espace, ou peut-être que je n'ai pas la permission), et pas pour le contrôle du flux.

normalement, je traite les exceptions comme:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

Ou si j'ai vraiment pas envie de retourner quoi que ce soit si une exception se produit, puis:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception
295
demandé sur Aaron Hall 2013-04-22 05:44:52

9 réponses

"je ne sais pas si c'est de l'ignorance, mais je n'aime pas qu' type de programmation, car il utilise des exceptions pour effectuer le contrôle de flux."

dans le monde de Python, l'utilisation d'exceptions pour le contrôle de flux est courante et normale.

même les développeurs de Python core utilisent des exceptions pour le contrôle du flux et ce style est fortement noyé dans le langage (i.e. le protocole iterator utilise Arrêt pour la terminaison de la boucle de signalisation).

de plus, le try-except-style est utilisé pour prévenir les conditions de course inhérentes à certaines des constructions " look-before-you-leap " . Par exemple, l'essai os.chemin.existe donne des renseignements qui peuvent être périmés au moment où vous les utilisez. De Même, Queue.full retourne des informations qui peuvent être obsolètes. Le try-except-else style produira un code plus fiable dans ces cas.

"Il ma compréhension que les exceptions ne sont pas des erreurs, ils ne doivent être utilisé dans des conditions exceptionnelles "

dans certaines autres langues, cette règle reflète leurs normes culturelles telles qu'elles sont reflétées dans leurs bibliothèques. La "règle" est également fondée en partie sur des considérations de rendement pour ces langues.

la norme culturelle Python est quelque peu différente. Dans de nombreux cas, vous doit utiliser des exceptions pour le contrôle-débit. En outre, l'utilisation d'exceptions en Python ne ralentit pas le code environnant et le code appelant comme il le fait dans certaines langues compilées (par exemple CPython implémente déjà le code pour la vérification d'exception à chaque étape, que vous utilisiez ou non des exceptions).

In autrement dit, votre compréhension que "les exceptions sont pour l'exceptionnel" est une règle qui a du sens dans d'autres langues, mais pas pour Python.

" cependant, si elle est incluse dans la langue elle-même, il doit y avoir une bonne raison pour ça, n'est-ce pas?"

en plus d'aider à éviter les conditions de course, les exceptions sont également très utiles pour tirer les boucles extérieures de manipulation d'erreur. C'est une optimisation nécessaire dans l'interprétation langues qui n'ont pas tendance à avoir automatique boucle invariant code mouvement .

en outre, les exceptions peuvent simplifier le code un peu dans les situations courantes où la capacité de traiter un problème est très éloignée de l'endroit où le problème est survenu. Par exemple, il est courant d'avoir du code d'appel d'interface utilisateur de haut niveau pour la logique d'affaires qui, à son tour, appelle des routines de bas niveau. Les Situations se produisant dans les routines de bas niveau (telles que les enregistrements en double pour unique les clés dans les accès à la base de données) ne peuvent être manipulées qu'en code de haut niveau (comme demander à l'utilisateur une nouvelle clé qui n'entre pas en conflit avec les clés existantes). L'utilisation d'exceptions pour ce type de contrôle de flux permet un niveau moyen de routines d'ignorer complètement le problème et bien découplé de cet aspect de contrôle de flux.

il y a un bel article de blog sur le caractère indispensable des exceptions ici .

Voir aussi Réponse: est-ce que les exceptions sont vraiment pour les erreurs exceptionnelles?

"Quelle est la raison pour l'essayer, sauf d'autre pour exister?"

la clause else elle-même est intéressante. Il court quand il n'y a pas d'exception, mais avant la clause finale. C'est son but premier.

sans la clause else, la seule option pour exécuter du code supplémentaire avant la finalisation serait la pratique maladroite d'ajouter le code à la clause d'essai. C'est maladroit car il risque en soulevant des exceptions dans le code qui n'étaient pas destinées à être protégées par le try-block.

le cas d'utilisation de l'exécution de code non protégé supplémentaire avant la finalisation ne se présente pas très souvent. Donc, ne vous attendez pas à voir beaucoup d'exemples dans le code publié. Il est assez rare.

un autre cas d'utilisation pour la clause else est d'effectuer des actions qui doivent se produire quand aucune exception se produit et qui ne se produisent pas lorsque les exceptions sont gérées. Par exemple:

   recip = float('Inf')
   try:
       recip = 1 / f(x)
   except ZeroDivisionError:
       logging.info('Infinite result')
   else:
       logging.info('Finite result')

enfin, L'utilisation la plus courante d'une clause else dans un try-block est pour un peu d'embellissement (alignement des résultats exceptionnels et des résultats non exceptionnels au même niveau d'indentation). Cette utilisation est toujours facultative et n'est pas strictement nécessaire.

449
répondu Raymond Hettinger 2018-07-31 01:10:48

Quelle est la raison de l'essai-sauf-pour exister?

un bloc try vous permet de gérer une erreur attendue. Le bloc except ne doit saisir que les exceptions que vous êtes prêt à gérer. Si vous gérez une erreur inattendue, votre code peut faire la mauvaise chose et cacher des bogues.

une clause else s'exécute s'il n'y a pas d'erreurs, et en n'exécutant pas ce code dans le try bloc, vous éviter d'attraper une erreur inattendue. Encore une fois, attraper une erreur inattendue peut cacher des bogues.

exemple

par exemple:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

la suite "essayer, sauf" comporte deux clauses facultatives, else et finally . Donc c'est en fait try-except-else-finally .

else n'est évalué que s'il n'y a pas d'exception au bloc try . Il nous permet de simplifier le plus code compliqué ci-dessous:

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

donc si nous comparons un else à l'alternative (qui pourrait créer des bogues) nous voyons qu'il réduit les lignes de code et nous pouvons avoir un code-base plus lisible, maintenable, et moins bogué.

finally

finally s'exécutera quoi qu'il arrive, même si une autre ligne est évaluée avec une instruction de retour.

ventilé avec pseudo-code

il pourrait aider à décomposer cela, dans la plus petite forme possible qui montre toutes les caractéristiques, avec des commentaires. Supposons que ce pseudo-code syntaxiquement correct (mais non exécutable à moins que les noms ne soient définis) soit dans une fonction.

par exemple:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

il est vrai que nous pourrions inclure le code dans le bloc else dans le bloc try à la place, où il serait courir s'il n'y avait pas d'exceptions, mais que faire si ce code lui-même soulève une exception du genre que nous attrapons? Le laisser dans le bloc try cacherait ce bug.

nous voulons minimiser les lignes de code dans le bloc try pour éviter d'attraper des exceptions auxquelles nous ne nous attendions pas, en vertu du principe que si notre code échoue, nous voulons qu'il échoue bruyamment. Il s'agit d'une meilleure pratique .

c'est mon comprendre que les exceptions ne sont pas des erreurs

en Python, la plupart des exceptions sont des erreurs.

nous pouvons voir la hiérarchie des exceptions en utilisant pydoc. Par exemple, en Python 2:

$ python -m pydoc exceptions

ou Python 3:

$ python -m pydoc builtins

nous donnera la hiérarchie. Nous pouvons voir que la plupart des types de Exception sont des erreurs, bien que Python utilise certains d'entre eux pour des choses comme la fin des boucles for ( StopIteration ). C'est la hiérarchie de Python 3:

BaseException
    Exception
        ArithmeticError
            FloatingPointError
            OverflowError
            ZeroDivisionError
        AssertionError
        AttributeError
        BufferError
        EOFError
        ImportError
            ModuleNotFoundError
        LookupError
            IndexError
            KeyError
        MemoryError
        NameError
            UnboundLocalError
        OSError
            BlockingIOError
            ChildProcessError
            ConnectionError
                BrokenPipeError
                ConnectionAbortedError
                ConnectionRefusedError
                ConnectionResetError
            FileExistsError
            FileNotFoundError
            InterruptedError
            IsADirectoryError
            NotADirectoryError
            PermissionError
            ProcessLookupError
            TimeoutError
        ReferenceError
        RuntimeError
            NotImplementedError
            RecursionError
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

un commentateur a demandé:

dites que vous avez une méthode qui envoie une API externe et que vous voulez gérer l'exception dans une classe en dehors de L'enveloppe de L'API, est-ce que vous renvoyez simplement e de la méthode sous la clause except où e est l'objet d'exception?

Non, vous n'avez pas de retour de l'exception, juste à sur-relancer avec un nu raise pour préserver le stacktrace.

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

ou, en Python 3, vous pouvez soulever une nouvelle exception et préserver le backtrace avec le chaînage d'exception:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

je développe dans ma réponse ici .

112
répondu Aaron Hall 2018-07-31 01:14:06

Python ne souscrit pas à l'idée que les exceptions ne devraient être utilisées que dans des cas exceptionnels, en fait l'idiome est "demander pardon, pas la permission" . Cela signifie que l'utilisation d'exceptions comme partie de routine de votre contrôle de flux est parfaitement acceptable, et en fait, encouragé.

c'est généralement une bonne chose, car travailler de cette façon permet d'éviter certains problèmes (comme un exemple évident, les conditions de course sont souvent évitées), et il a tendance à rendre le code un peu plus lisible.

Imaginez que vous avez une situation où vous prenez une entrée utilisateur qui doit être traitée, mais que vous avez une entrée par défaut qui est déjà traitée. La structure try: ... except: ... else: ... rend le code très lisible:

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

comparez à la façon dont il pourrait fonctionner dans d'autres langues:

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

notez les avantages. Il n'est pas nécessaire de vérifier la valeur est valide et l'analyser séparément, ils sont fait une fois. Le code suit également une progression plus logique, le chemin principal du code est le premier, suivi de "si ça ne marche pas, faites ceci".

L'exemple est naturellement un peu artificiel, mais il montre qu'il existe des cas pour cette structure.

28
répondu Gareth Latty 2013-04-22 01:59:14

est-ce une bonne pratique d'utiliser try-except-else en python?

la réponse à cette question Est qu'elle dépend du contexte. Si vous faites cela:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

cela démontre que vous ne connaissez pas très bien Python. Cette fonctionnalité est encapsulée dans la méthode dict.get :

item = d.get('item', 'default')

le bloc try / except est beaucoup plus encombré visuellement et verbeuse façon d'écrire ce qui peut être exécuté efficacement dans une seule ligne avec une méthode atomique. Il y a d'autres cas où c'est vrai.

Toutefois, cela ne signifie pas que nous devrions éviter toute manipulation d'exception. Dans certains cas, il est préférable d'éviter des conditions de course. Ne Vérifiez pas si un fichier existe, essayez juste de l'ouvrir, et attrapez l'IOError approprié. Pour des raisons de simplicité et de lisibilité, essayez de l'encapsuler ou de le prendre en compte.

lire le Zen de Python , en comprenant qu'il ya des principes qui sont en tension, et méfiez-vous du dogme qui repose trop lourdement sur l'un des énoncés en elle.

14
répondu Aaron Hall 2015-02-03 16:54:25

Vous devez être prudent sur l'utilisation du bloc finally, ce n'est pas la même chose que d'utiliser un autre bloc dans l'essai, à l'exception de. Le bloc final sera exécuté quel que soit le résultat de l'essai sauf.

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

comme tout le monde l'a noté en utilisant le bloc else rend votre code plus lisible, et ne s'exécute que lorsqu'une exception n'est pas lancée

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:
4
répondu Greg 2013-04-24 00:53:17

chaque fois que vous voyez ceci:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

ou même ceci:

try:
    return 1 / x
except ZeroDivisionError:
    return None

au lieu de:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x
4
répondu Rajiv Bakulesh Shah 2017-01-13 23:40:49

c'est mon petit détail simple sur la façon de comprendre try-sauf-else-enfin bloquer en Python:

def div(a, b):
    try:
        a/b
    except ZeroDivisionError:
        print("Zero Division Error detected")
    else:
        print("No Zero Division Error")
    finally:
        print("Finally the division of %d/%d is done" % (a, b))

essayons div 1/1:

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

essayons div 1/0

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done
1
répondu zakiakhmad 2018-05-06 06:24:45

voir l'exemple suivant qui illustre tout sur try-sauf-else-finally:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

L'implémenter et venir:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.
0
répondu JawSaw 2018-08-25 05:04:02

OP, VOUS AVEZ RAISON. L'autre après le try/except en Python, c'est moche . elle conduit à un autre objet de régulation du débit là où aucun n'est nécessaire:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

un équivalent totalement clair est:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

c'est beaucoup plus clair qu'une autre clause. L'autre chose après try/except n'est pas souvent écrit, il faut un moment pour comprendre ce que seront les conséquences.

juste parce que vous pouvez fais quelque chose, ça ne veut pas dire que tu dois faire quelque chose.

beaucoup de fonctionnalités ont été ajoutées aux langues parce que quelqu'un a pensé que cela pourrait être utile. Le problème, c'est que plus il y a de traits, moins les choses sont claires et évidentes parce que les gens n'utilisent pas habituellement ces cloches et sifflets.

juste mes 5 cents. Je dois venir derrière et nettoyer beaucoup de code écrit par les développeurs de première année de l'université qui pensent qu'ils sont intelligents et veulent Ecrire le code d'une manière Uber-tight, uber-efficace quand cela le rend juste un gâchis pour essayer et lire / modifier plus tard. Je vote pour la lisibilité tous les jours et deux fois le dimanche.

-3
répondu Kevin J. Rice 2015-03-20 16:03:11