184 votes

"Enfin" est-il toujours exécuté en Python?

Pour tout possible, essayez-bloc finally en Python, est-il garanti que l' finally bloc sera toujours exécutée?

Par exemple, disons que je retourne en except bloc:

try:
    1/0
except ZeroDivisionError:
    return
finally:
    print("Does this code run?")

Ou peut-être que je sur-relance un Exception:

try:
    1/0
except ZeroDivisionError:
    raise
finally:
    print("What about this code?")

Le test indique que l' finally n'est exécuté que pour les exemples ci-dessus, mais j'imagine qu'il y a d'autres scénarios que je n'ai pas pensé.

Existe-il des scénarios dans lesquels un finally bloc peut ne pas s'exécuter en Python?

276voto

user2357112 Points 37737

"Garantie" est beaucoup plus de mots que la mise en œuvre d' finally mérite. Ce qui est garanti, c'est que si l'exécution de flux de l'ensemble de l' try-finally construire, il va passer à travers l' finally à le faire. Ce n'est pas garanti, c'est que l'exécution s'écouler hors de l' try-finally.

  • Un finally dans un générateur ou asynchrone coroutine pourrait jamais fonctionner, si l'objet ne s'exécute jamais de conclusion. Il y a beaucoup de façons qui pourraient se produire; en voici un:

    def gen(text):
        try:
            for line in text:
                try:
                    yield int(line)
                except:
                    # Ignore blank lines - but catch too much!
                    pass
        finally:
            print('Doing important cleanup')
    
    text = ['1', '', '2', '', '3']
    
    if any(n > 1 for n in gen(text)):
        print('Found a number')
    
    print('Oops, no cleanup.')
    

    Notez que cet exemple est un peu difficile: lorsque le générateur est des ordures collectées, Python tente d'exécuter l' finally bloc en le jetant dans une GeneratorExit d'exception, mais ici nous attraper cette exception et puis, yield encore une fois, à quel point Python affiche un message d'avertissement ("générateur ignoré GeneratorExit") et abandonne. Voir PEP 342 (Coroutines via une meilleure Générateurs) pour plus de détails.

    D'autres moyens d'une génératrice ou d'coroutine peut pas exécuter à la conclusion comprennent si l'objet est jamais GC ed (oui, c'est possible, même dans Disponible), ou si un async with awaits dans __aexit__, ou si l'objet awaits ou yields en finally le bloc. Cette liste n'est pas destinée à être exhaustive.

  • Un finally dans un fil de démon ne pourrait jamais exécuter si tous les non-fils de démon sortir en premier.

  • os._exit s'arrête immédiatement le processus sans l'exécution d' finally blocs.

  • os.fork pourrait provoquer finally blocs à exécuter deux fois. Ainsi que tout les problèmes que vous attendez de passe des choses deux fois, ce qui pourrait provoquer des accès simultanés conflits (accidents, stands, ...) si l'accès aux ressources partagées n'est pas correctement synchronisé.

    Depuis multiprocessing utilise la fourche-sans-exec pour créer des processus de travail lors de l'utilisation de la fourche méthode de démarrage (par défaut sous Unix), puis appelle os._exit chez le travailleur une fois l'emploi du travailleur est fait, finally et multiprocessing interaction peut être problématique (exemple).

  • Un C-niveau de segmentation fault empêcher finally pâtés de maisons de l'exécution.
  • kill -SIGKILL empêchera finally pâtés de maisons de l'exécution. SIGTERM et SIGHUP permettra également d'éviter l' finally pâtés de maisons de l'exécution, sauf si vous installez un gestionnaire de contrôler l'arrêt vous-même; par défaut, Python n'a pas de poignée SIGTERM ou SIGHUP.
  • Une exception dans finally peut empêcher le nettoyage de se terminer. A noter le cas est si l'utilisateur appuie sur ctrl-C juste que nous commençons à exécuter l' finally bloc. Python va soulever une KeyboardInterrupt et passer chaque ligne de l' finally bloc de contenu. (KeyboardInterrupt-code de sécurité est très dur à écrire).
  • Si l'ordinateur perd de la puissance, ou si elle hiberne et ne se réveille pas, finally blocs ne fonctionne pas.

L' finally bloc n'est pas un système de transaction; il ne prévoit pas d'atomicité des garanties ou quoi que ce soit de la sorte. Certains de ces exemples peut sembler évident, mais il est facile d'oublier que de telles choses peuvent se produire et de s'appuyer sur finally de trop.

95voto

wim Points 35274

Oui. Enfin gagne toujours.

Le seul moyen de le vaincre est de mettre fin à l'exécution avant l' finally: a la chance de s'exécuter (par exemple, le plantage de l'interprète, éteignez votre ordinateur, de suspendre un générateur à jamais).

J'imagine qu'il y a d'autres scénarios que je n'ai pas pensé.

Ici sont un couple de plus vous pouvez ne pas avoir pensé à:

def foo():
    # finally always wins
    try:
        return 1
    finally:
        return 2

def bar():
    # even if he has to eat an unhandled exception, finally wins
    try:
        raise Exception('boom')
    finally:
        return 'no boom'

Selon la façon dont vous quittez l'interprète, parfois, vous pouvez "annuler" enfin, mais pas à ce point:

>>> import sys
>>> try:
...     sys.exit()
... finally:
...     print('finally wins!')
... 
finally wins!
$

À l'aide de la précarité de l' os._exit (ce qui relève de "crash de l'interprète", à mon avis):

>>> import os
>>> try:
...     os._exit(1)
... finally:
...     print('finally!')
... 
$

Je suis actuellement en cours d'exécution du présent code, pour tester si finalement exécute tout de même après la mort thermique de l'univers:

try:
    while True:
       sleep(1)
finally:
    print('done')

Cependant, je suis toujours en attente sur le résultat, alors revenez ici plus tard.

16voto

jayce Points 193

Selon la documentation Python:

Peu importe ce qui s'est passé auparavant, le dernier bloc est exécuté une fois que le bloc de code est complet et tout ont soulevé des exceptions traitées. Même si il y a une erreur dans un gestionnaire d'exception ou l'autre bloc et une nouvelle exception est levée, le code dans le dernier bloc est toujours exécuté.

Il convient également de noter que s'il y a plusieurs instructions return, dont l'une dans le bloc finally, le bloc finally retour, c'est le seul qui va s'exécuter.

13voto

Serge Ballesta Points 12850

Eh bien, oui et non.

Ce qui est garanti, c'est que Python va toujours essayer d'exécuter le bloc finally. Dans le cas où vous le retour de la bloquer ou de soulever une exception non interceptée, le bloc finally est exécuté juste avant de le retourner ou de la levée de l'exception.

(ce que vous avez pu vous-même contrôlée par la simple exécution de code dans votre question)

Le seul cas que je peux imaginer d'où le bloc finally ne sera pas exécuté, c'est quand le Python interprète lui-même se bloque par exemple à l'intérieur du code C ou en raison de panne de courant.

-3voto

Basj Points 776

Pour vraiment comprendre son fonctionnement, exécutez ces deux exemples:

  •  try:
        1
    except:
        print 'except'
    finally:
        print 'finally'
     

    va sortir

    enfin

  •  try:
        1/0
    except:
        print 'except'
    finally:
        print 'finally'
     

    va sortir

    sauf
    enfin

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X