"Je ne sais pas si c'est par ignorance, mais je n'aime pas ce genre de programmation, car il utilise des exceptions pour effectuer le contrôle de flux."
Dans le monde Python, l'utilisation d'exceptions pour le contrôle de flux est courante et normale.
Même les développeurs principaux de Python utilisent des exceptions pour le contrôle de flux et ce style est fortement intégré dans le langage (c'est-à-dire que le protocole itérateur utilise StopIteration pour signaler la fin de la boucle).
En outre, le style try-except-est utilisé pour empêcher les conditions de concurrence inhérentes à certaines des constructions "à regarder avant de sauter" . Par exemple, le test de os.path.exists entraîne des informations qui peuvent être obsolètes au moment où vous les utilisez. De même, Queue.full renvoie des informations qui peuvent être périmées. Le style try-except-else produira du code plus fiable dans ces cas.
"Je crois comprendre que les exceptions ne sont pas des erreurs, elles ne doivent être utilisées que dans des conditions exceptionnelles"
Dans certaines autres langues, cette règle reflète leurs normes culturelles telles que reflétées dans leurs bibliothèques. La "règle" est également basée en partie sur des considérations de performances pour ces langues.
La norme culturelle Python est quelque peu différente. Dans de nombreux cas, vous devez utiliser des exceptions pour le flux de contrôle. En outre, l'utilisation d'exceptions en Python ne ralentit pas le code environnant et le code appelant comme il le fait dans certains langages compilés (c'est-à-dire que CPython implémente déjà du code pour la vérification des exceptions à chaque étape, que vous utilisiez ou non des exceptions).
En d'autres termes, votre compréhension que "les exceptions sont pour l'exceptionnel" est une règle qui a du sens dans certains autres langages, mais pas pour Python.
"Cependant, s'il est inclus dans la langue elle-même, il doit y avoir une bonne raison, n'est-ce pas?"
En plus d'aider à éviter les conditions de concurrence, les exceptions sont également très utiles pour tirer des boucles extérieures de gestion des erreurs. Il s'agit d'une optimisation nécessaire dans les langages interprétés qui n'ont pas tendance à avoir un mouvement de code invariant de boucle automatique .
En outre, les exceptions peuvent simplifier un peu le code dans des situations courantes où la possibilité de gérer un problème est très éloignée de l'origine du problème. Par exemple, il est courant d'avoir un code d'appel de code d'interface utilisateur de niveau supérieur pour la logique métier qui à son tour appelle des routines de bas niveau. Les situations survenant dans les routines de bas niveau (telles que les enregistrements en double pour des clés uniques dans les accès à la base de données) ne peuvent être gérées que dans le code de niveau supérieur (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 flux de contrôle permet aux routines de niveau intermédiaire d'ignorer complètement le problème et d'être bien dissociées de cet aspect du contrôle de flux.
Il y a un joli billet de blog sur le caractère indispensable des exceptions ici .
Voir également cette réponse Stack Overflow: les exceptions sont-elles vraiment des erreurs exceptionnelles?
"Quelle est la raison pour laquelle le try-except-else existe?"
La clause else elle-même est intéressante. Il s'exécute lorsqu'il n'y a pas d'exception mais avant la clause finally. Tel est son objectif principal.
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 try. C'est maladroit car cela risque de soulever des exceptions dans du code qui n'était pas destiné à être protégé par le bloc d'essai.
Le cas d'utilisation de l'exécution de code supplémentaire non protégé avant la finalisation ne se pose pas très souvent. Donc, ne vous attendez pas à voir de nombreux exemples dans le code publié. C'est assez rare.
Un autre cas d'utilisation de la clause else consiste à effectuer des actions qui doivent se produire lorsqu'aucune exception ne se produit et qui ne se produisent pas lorsque des 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')
Un autre exemple se produit chez les coureurs les moins performants:
try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')
Enfin, l'utilisation la plus courante d'une clause else dans un bloc d'essai est pour un peu d'embellissement (en alignant les résultats exceptionnels et les résultats non exceptionnels au même niveau d'indentation). Cette utilisation est toujours facultative et n'est pas strictement nécessaire.
Un
try
bloc vous permet de gérer une erreur attendue. Leexcept
bloc ne doit intercepter 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 masquer les bogues.Une
else
clause s'exécutera s'il n'y a pas eu d'erreurs, et en n'exécutant pas ce code dans letry
bloc, vous éviterez d'attraper une erreur inattendue. Encore une fois, la capture d'une erreur inattendue peut masquer les bogues.Exemple
Par exemple:
La suite "try, except" a deux clauses facultatives,
else
etfinally
. C'est donc en faittry-except-else-finally
.else
n'évaluera que s'il n'y a pas d'exception dutry
bloc. Cela nous permet de simplifier le code plus compliqué ci-dessous: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 une base de code plus lisible, plus facile à entretenir et moins boguée.finally
finally
s'exécutera quoi qu'il arrive, même si une autre ligne est évaluée avec une instruction de retour.Décomposé avec un pseudo-code
Cela pourrait aider à décomposer cela, dans la forme la plus petite possible qui montre toutes les fonctionnalités, avec des commentaires. Supposons que ce pseudo-code syntaxiquement correct (mais non exécutable sauf si les noms sont définis) se trouve dans une fonction.
Par exemple:
Il est vrai que nous pourrions inclure le code dans le
else
bloc dans letry
bloc à la place, où il s'exécuterait s'il n'y avait pas d'exceptions, mais que faire si ce code lui-même déclenche une exception du type que nous interceptons? Le laisser dans letry
bloc cacherait ce bug.Nous voulons minimiser les lignes de code dans le
try
bloc 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 .En Python, la plupart des exceptions sont des erreurs.
Nous pouvons afficher la hiérarchie des exceptions en utilisant pydoc. Par exemple, en Python 2:
ou Python 3:
Nous donnera la hiérarchie. Nous pouvons voir que la plupart des types d'
Exception
erreurs sont des erreurs, bien que Python en utilise certaines pour des choses comme la fin desfor
boucles (StopIteration
). Voici la hiérarchie de Python 3:Un intervenant a demandé:
Non, vous ne renvoyez pas l'exception, relancez-la simplement avec un nu
raise
pour conserver la trace de pile.Ou, dans Python 3, vous pouvez déclencher une nouvelle exception et conserver la trace avec chaînage d'exceptions:
J'élabore dans ma réponse ici .
la source
raise
chaînage d'exception nu ou faire - mais c'est plus sur le sujet et couvert ici: stackoverflow.com/q/2052390/541136 - je supprimerai probablement ces commentaires après avoir vu que vous les avez vus.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 permission» . Cela signifie que l'utilisation d'exceptions en tant que partie de routine de votre contrôle de flux est parfaitement acceptable et, en fait, encouragée.
C'est généralement une bonne chose, car travailler de cette façon permet d'éviter certains problèmes (comme exemple évident, les conditions de concurrence sont souvent évitées), et cela tend à rendre le code un peu plus lisible.
Imaginez que vous ayez une situation où vous prenez une entrée utilisateur qui doit être traitée, mais avez une valeur par défaut qui est déjà traitée. La
try: ... except: ... else: ...
structure rend le code très lisible:Comparez avec la façon dont cela pourrait fonctionner dans d'autres langues:
Notez les avantages. Il n'est pas nécessaire de vérifier que la valeur est valide et de l'analyser séparément, elles sont effectuées une fois. Le code suit également une progression plus logique, le chemin du code principal est le premier, suivi de «si cela ne fonctionne pas, faites-le».
L'exemple est naturellement un peu artificiel, mais il montre qu'il y a des cas pour cette structure.
la source
La réponse à cela est qu'elle dépend du contexte. Si tu fais ça:
Cela montre que vous ne connaissez pas très bien Python. Cette fonctionnalité est encapsulée dans la
dict.get
méthode:Le
try
/except
bloc est une façon beaucoup plus visuellement encombrée et verbeuse d'écrire ce qui peut être exécuté efficacement sur une seule ligne avec une méthode atomique. Il y a d'autres cas où cela est vrai.Cependant, cela ne signifie pas que nous devons éviter toute gestion des exceptions. Dans certains cas, il est préférable d'éviter les conditions de course. Ne vérifiez pas si un fichier existe, essayez simplement de l'ouvrir et interceptez l'erreur IOError appropriée. Dans un souci de simplicité et de lisibilité, essayez de l'encapsuler ou de le factoriser comme il convient.
Lisez le Zen de Python , en comprenant qu'il y a des principes qui sont en tension, et méfiez-vous des dogmes qui reposent trop fortement sur l'une des déclarations qu'il contient.
la source
Voir l'exemple suivant qui illustre tout sur try-except-else-finally:
Implémentez-le et venez par:
la source
Vous devez faire attention à utiliser le bloc finally, car ce n'est pas la même chose que d'utiliser un bloc else dans l'essai, sauf. Le bloc finalement sera exécuté indépendamment du résultat de l'essai sauf.
Comme tout le monde l'a noté, l'utilisation du bloc else rend votre code plus lisible et ne s'exécute que lorsqu'une exception n'est pas levée
la source
Chaque fois que vous voyez ceci:
Ou même ceci:
Considérez ceci à la place:
la source
Juste parce que personne d'autre n'a publié cette opinion, je dirais
Contrairement aux mots - clés
try
,except
etfinally
, le sens de laelse
clause est pas de soi; c'est moins lisible. Parce qu'il n'est pas utilisé très souvent, les personnes qui liront votre code voudront revérifier les documents pour s'assurer qu'elles comprennent ce qui se passe.(J'écris cette réponse précisément parce que j'ai trouvé un
try/except/else
dans ma base de code et cela a causé un moment wtf et m'a forcé à faire une recherche sur Google).Donc, partout où je vois du code comme l'exemple OP:
Je préférerais refactoriser
<1> retour explicite, montre clairement que, dans le cas exceptionnel, nous avons fini de travailler
<2> comme un effet secondaire mineur agréable, le code qui était dans le
else
bloc est déduit d'un niveau.la source
Voici mon simple extrait sur la façon de comprendre le bloc try-except-else-finally en Python:
Essayons div 1/1:
Essayons div 1/0
la source
OP, VOUS ÊTES CORRECT. Le reste après try / except en Python est moche . cela conduit à un autre objet de contrôle de flux où aucun n'est nécessaire:
Un équivalent totalement clair est:
C'est beaucoup plus clair qu'une clause else. Le reste après try / except n'est pas souvent écrit, donc cela prend un moment pour comprendre quelles sont les implications.
Ce n'est pas parce que vous POUVEZ faire quelque chose que vous DEVRIEZ faire quelque chose.
Beaucoup de fonctionnalités ont été ajoutées aux langues parce que quelqu'un pensait que cela pourrait être utile. Le problème, c'est que plus il y a de fonctionnalités, moins les choses sont claires et évidentes parce que les gens n'utilisent généralement pas ces cloches et ces sifflets.
Juste mes 5 cents ici. Je dois venir derrière et nettoyer beaucoup de code écrit par des développeurs de collège de première année qui pensent qu'ils sont intelligents et veulent écrire du code d'une manière ultra serrée et ultra efficace quand cela en fait un gâchis pour essayer de lire / modifier plus tard. Je vote pour la lisibilité tous les jours et deux fois le dimanche.
la source
print
déclaration qui échoue. Que se passe-t-il six = blah()
renvoie unstr
, mais votre relevé d'impression l'estprint 'just succeeded with blah. x == %d' % x
? Vous avez maintenantTypeError
généré un être où vous n'êtes pas prêt à en gérer un; vous inspectezx = blah()
pour trouver la source de l'exception, et ce n'est même pas là. J'ai fait cela (ou l'équivalent) plus d'une fois où celaelse
m'aurait empêché de faire cette erreur. Maintenant je sais mieux. :-Delse
clause n'est pas une jolie déclaration, et tant que vous n'y êtes pas habituée, elle n'est pas intuitive. Mais alors, ce n'était pas le casfinally
quand j'ai commencé à l'utiliser ...else
clause ne sont pas prises en compte par leexcept
.x = blah()
pour trouver la source de l'exception",traceback
Vous vous demandez pourquoi vous inspecteriez la source de l'exception à un mauvais endroit?