Pourquoi `continue` n’est pas autorisé dans une clause` finally` dans Python?

Le code suivant génère une erreur de syntaxe:

>>> for i in range(10): ... print i ... try: ... pass ... finally: ... continue ... print i ... File "", line 6 SyntaxError: 'continue' not supported inside 'finally' clause 

Pourquoi une déclaration continue n’est-elle pas autorisée dans une clause finally ?

PS D’autre part, cet autre code n’a pas de problèmes:

 >>> for i in range(10): ... print i ... try: ... pass ... finally: ... break ... 0 

Si c’est important, j’utilise Python 2.6.6.

L’utilisation de continuer dans une clause-finale est interdite car son interprétation aurait été problématique. Que feriez-vous si la clause finally était exécutée à cause d’une exception?

 for i in range(10): print i try: raise RuntimeError finally: continue # if the loop continues, what would happen to the exception? print i 

Il nous est possible de prendre une décision sur ce que ce code devrait faire, peut-être en avalant l’exception; mais une bonne conception linguistique suggère le contraire. Si le code confond les lecteurs ou s’il existe un moyen plus clair d’exprimer la logique voulue (peut-être avec try: ... except Exception: pass; continue ), il y a un avantage à laisser cela comme une SyntaxError .

Fait intéressant, vous pouvez placer un retour dans une clause finally et il engloutira toutes les exceptions, y compris KeyboardInterrupt , SystemExit et MemoryError . Ce n’est probablement pas une bonne idée non plus 😉

Le langage de référence Python interdit l’utilisation de continue dans une clause finally . Je ne sais pas trop pourquoi. Peut-être parce que continue dans la clause try garantit que le finally est exécuté, et décider de ce qui doit continue dans la clause finally est quelque peu ambigu.

Edit: Le commentaire de @Mike Christensen à la question met en évidence un fil où l’ambiguïté de cette construction est discutée par les développeurs de Python. De plus, en plus de neuf ans d’utilisation de Python, je n’ai jamais voulu le faire, donc c’est probablement une situation relativement peu commune que les développeurs n’ont pas envie de passer beaucoup de temps.

Je pense que la raison en est assez simple. L’instruction continue après le mot-clé finally est exécutée à chaque fois. C’est la nature de la déclaration Enfin. Que votre code déclenche ou non une exception est sans importance. Enfin sera exécuté.

Par conséquent, votre code …

 for i in range(10): print i try: pass finally: continue print i # this (and anything else below the continue) won't ever be executed! 

est équivalent à ce code …

 for i in range(10: print i try: pass finally: pass 

qui est plus propre et terser. Python ne permet pas de continuer dans un bloc finally car tout le code après le continu ne sera jamais exécuté. (La densité est meilleure que la densité.)

Je ne l’ai pas vu mentionné dans une autre réponse, mais je pense que ce que vous pourriez vouloir dans ce cas est try..else :

 for i in range(10): print i try: #pass <= I commented this out! do_something_that_might_fail(i) except SomeException: pass else: continue print i 

Le bloc else n'est exécuté que s'il n'y a pas d'exception. Donc, cela signifie que:

  1. Nous print i
  2. Nous try de do_something_that_might_fail(i)
  3. Si elle jette SomeException , tomber et print i nouveau
  4. Sinon, on continue (et i ne i jamais imprimé)

La possibilité d’obtenir une exception puis de la déglutir simplement parce que vous utilisez un continue est un argument fort, mais l’exception est également avalée lorsque vous utilisez plutôt un return ou une break .

Par exemple, cela fonctionne et l’exception est avalée:

 for i in range(10): print i try: raise Exception finally: break print i # not gonna happen 

Cela fonctionne à nouveau sans erreur (dans une fonction) et l’exception est également avalée:

 for i in range(10): print i try: raise Exception finally: return print i # not gonna happen 

Alors, pourquoi break et return serait-il autorisé dans un bloc finally , avec ou sans éventuelles erreurs soulevées, mais continue non?

Vous pouvez également envisager la combinaison des facteurs suivants dans le problème:

  • finally est toujours exécuté;
  • continue “abandonne” l’itération en cours.

Cela signifie que dans chaque boucle, à cause de l’exécution toujours permanente, vous aurez toujours une continue qui dit fondamentalement “Abandonner l’itération en cours”, “Abandonner l’itération en cours”, “Abandonner l’itération en cours” … aucun sens. Mais cela n’a pas de sens d’utiliser le return break . L’itération actuelle est également annulée, à la seule différence que vous vous retrouvez maintenant avec une seule itération.

Donc, la question “Pourquoi continue t-elle est interdite dans un finally ?” peut également être demandé comme “Pourquoi la break et le return autorisés?”.

Peut-être parce que cela avait du sens de ne pas le faire à ce moment-là? C’était la décision des développeurs et maintenant c’est comme ça? Bien sûr, cela pourrait aussi être la paresse de l’implémenteur, mais qui sait, peut-être qu’ils avaient quelque chose en tête et peut-être que, dans une autre version de Python, il serait plus judicieux de l’avoir autrement?

L’idée est que les exemples ici sont juste extrêmes . Tu n’écris pas simplement du code comme ça, n’est-ce pas? Il y a sûrement une certaine logique dans le bloc finally à dire quand break/return/continue , peu importe, et pas seulement le voir émoussé comme ça. En tant que tel, IMHO continue intérieur d’un finally devrait être autorisé car j’apprécierais d’écrire un code propre avec using continue in finally si c’est ce dont j’ai besoin, au lieu d’avoir recours à une solution de code pour cette limitation (dans la philosophie de Python ” adultes consentants ici “).

Une instruction continue était illégale dans la clause finally à cause d’un problème d’implémentation. Dans Python 3.8, cette ressortingction a été levée.

Le bogue était issue32489 – Autoriser la clause ‘continue’ in ‘finally’ .

La demande d’extraction pour le correctif: https://github.com/serhiy-storchaka/cpython/pull/2