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
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.
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 .
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.
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.
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"
....:
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
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
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.
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.