¿’Por fin’ siempre se ejecutan en Python?

Para cualquier posible probar-por último bloque en Python, se garantiza que el finally bloque siempre se ejecutará?

Por ejemplo, digamos que yo regrese, mientras que en un except bloque:

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

O tal vez me re-subir una Exception:

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

Las pruebas muestran que finally no ser ejecutado por los ejemplos anteriores, pero me imagino que hay otros escenarios que no ha pensado.

Hay escenarios en los que un finally bloque puede no ejecutarse en Python?

  • El único caso que puedo imaginar finally no se ejecute o «derrotar a su propósito» es durante un bucle infinito, sys.exit o un obligó a interrumpir. El documentación estados que finally se ejecuta siempre, así que me gustaría ir con eso.
  • Un poco de pensamiento lateral y de seguro no se lo que me pediste, pero estoy bastante seguro de que si abres el Administrador de Tareas y matar el proceso, finally no se ejecutará. O el mismo si el ordenador se bloquea antes 😀
  • finally no se ejecutará si el cable de alimentación es arrancado de la pared.
  • Usted puede estar interesado en esta respuesta a la misma pregunta acerca de C#: stackoverflow.com/a/10260233/88656
  • Bloque en vacío de un semáforo. Nunca señal. Hecho.
  • sys.exit no hace nada, pero lanzar una excepción. Muy por el nombre equivocado.

6 Kommentare

  1. 174

    «Garantizado» es mucho más fuerte de la palabra de cualquier aplicación de finally merece. Lo que está garantizado es que si la ejecución de los flujos de salida de toda la tryfinally construir, va a pasar a través de la finally para hacerlo. Lo que no está garantizado es que la ejecución del flujo de la tryfinally.

    • Un finalmente en un generador o async corutina no se ejecute, si el objeto nunca se ejecuta a una conclusión. Hay un montón de maneras en que podría suceder; he aquí uno:

      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.')

      Observe que en este ejemplo es un poco complicado: cuando el generador es la recolección, Python, intenta ejecutar el finally bloque por lanzar en un GeneratorExit excepción, pero aquí se captura la excepción y, a continuación, yield de nuevo, momento en el que Python imprime una advertencia («generador ignorado GeneratorExit») y se da por vencido. Ver PEP 342 (Corrutinas a través de una mayor Generadores) para más detalles.

      Otras maneras en que un generador o de la corutina no podría ejecutar a la conclusión de incluir si el objeto es sólo que nunca GC ed (que sí, que es posible, incluso en CPython), o si un async with awaits en __aexit__, o si el objeto awaits o yields en un finally bloque. Esta lista no pretende ser exhaustiva.

    • Un finalmente en un demonio hilo podría no ejecute nunca si todos los no-demonio de hilos de salida de la primera.

    • os._exit va a detener el proceso de inmediato sin ejecutar finally bloques.

    • os.fork puede causar finally bloques para ejecuta el dos veces. Así como sólo los problemas normales que usted esperaría de las cosas que suceden dos veces, esto podría causar que el acceso simultáneo de conflictos (accidentes, paradas, …) si el acceso a los recursos compartidos no es sincronizado correctamente.

      Desde multiprocessing usa tenedor-sin-exec para crear procesos de trabajo cuando se utiliza el horquilla método de inicio de la (el valor predeterminado en Unix), y, a continuación, llama os._exit en el trabajador una vez que el trabajo está hecho, finally y multiprocessing interacción puede ser problemático (ejemplo).

    • Un nivel C fallo de segmentación evitará finally bloques de ejecución.
    • kill -SIGKILL evitará finally bloques de ejecución. SIGTERM y SIGHUP también evitará que finally cuadras de ejecución a menos que instale un controlador para controlar el apagado de uno mismo; por defecto, Python no manejar SIGTERM o SIGHUP.
    • Una excepción en finally puede evitar la limpieza de finalización. Uno que es particularmente digno de mención el caso es que si el usuario pulsa control-C sólo como estamos empezando a ejecutar el finally bloque. Python se plantean una KeyboardInterrupt y saltar cada línea de la finally del bloque de contenidos. (KeyboardInterrupt-código seguro de que es muy difícil de escribir).
    • Si el equipo pierde el poder, o si hiberna y no despertar, finally bloques no se ejecuta.

    La finally bloque no es un sistema de transacción; no proporcionar la atomicidad garantiza ni nada por el estilo. Algunos de estos ejemplos podría parecer obvio, pero es fácil olvidarse de estas cosas pueden suceder y confiar en finally demasiado.

    • Creo que el primer punto de la lista es realmente relevante, y no hay una manera fácil de evitar: 1) no utilice nunca un desnudo de except, y nunca coger GeneratorExit en el interior de un generador. Los puntos sobre hilos/matar el proceso/segfaulting/apagado se espera, python no puede hacer magia. También: excepciones en finally son, obviamente, un problema, pero esto no cambia el hecho de que el flujo de control de fue se trasladó a la finally bloque. Con respecto a Ctrl+C, usted puede añadir un manejador de señal que ignora, o simplemente «horarios» un apagado limpio después de la operación actual se ha completado.
    • Evitar el vacío, con la excepción no es suficiente, porque de un par de las más complejas variantes del mismo tema. El bloque finally no puede ser llamado inmediatamente si el generador es parte de un ciclo de referencia (se va a esperar hasta la próxima colección, tiempo durante el cual el proceso podría haber salido), y antes de Python 3.4, podría no ser recaudados en todo, si en un ciclo de referencia con otro objeto con un finalizador.
    • La mención de kill -9 es técnicamente correcto, pero un poco injusta. No existe un programa escrito en cualquier lenguaje ejecuta el código en el momento de recibir un kill -9. De hecho, ningún programa recibe un kill -9 a todos, por lo que incluso si quisiera, no podría ejecutar nada. Ese es el punto de kill -9.
    • El punto sobre la kill -9 no especificar un idioma. Y, francamente, es necesario repetir, ya que se encuentra en un punto ciego. También mucha gente se olvida, o no se dan cuenta, que su programa podría dejar de muertos en sus pistas sin siquiera ser permitido para limpiar.
    • Hay gente confiando en finally bloques como si transaccional garantías. Parece obvio que no, pero no es algo que todo el mundo se da cuenta. Como para el generador caso, hay un montón de maneras en que un generador puede no ser GC ed todo, y un montón de maneras en que un generador o de la corutina podría accidentalmente rendimiento después de GeneratorExit incluso si no coger el GeneratorExit, por ejemplo, si un async with suspende una corutina en __exit__.
    • sí – he estado tratando durante décadas para llegar a los desarrolladores para limpiar los archivos temporales, etc. en aplicación de inicio, no de salida. Confiando en el llamado ‘limpia y elegante terminación’, está pidiendo la decepción y lágrimas:)

  2. 66

    Sí. Finalmente siempre gana.

    La única manera de vencer es para detener la ejecución antes de finally: obtiene una oportunidad para ejecutar (por ejemplo, accidente de la intérprete, apague el equipo, suspender a un generador para siempre).

    Me imagino que hay otros escenarios que no ha pensado.

    Aquí hay un par más que usted puede no haber pensado:

    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'

    Dependiendo de cómo salir de la intérprete, a veces puede «cancelar» por último, pero no como este:

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

    El uso de la precaria os._exit (esto entra en la categoría de «bloqueo de la intérprete de» en mi opinión):

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

    Actualmente estoy ejecutando este código, para probar si finalmente se va a ejecutar después de la muerte de calor del universo:

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

    Sin embargo, todavía estoy esperando el resultado, así que vuelve aquí más tarde.

    • o de tener un yo finito bucle en el try catch
    • finally en un generador o de la corutina puede muy fácilmente no se ejecute, sin ir a ninguna parte cerca de un «accidente de la intérprete de» condición.
    • Después de que el calor de la muerte de el universo, el tiempo deja de existir, así sleep(1) definitivamente resultar en un comportamiento indefinido. 😀
    • Usted puede querer hablar de _os.salida directamente después de «la única manera de vencer es para bloquear el compilador». Ahora es mixto inbeteween ejemplos donde finalmente gana.
    • Yo no creo que eso sea necesario – os._exit es, para todos los propósitos prácticos, la misma que la inducción de un accidente (inmundo de salida). La forma correcta de salir es sys.exit.
    • véase mi edición se deshacen – va a dejar este comentario en su lugar – como el aceptado respuesta muestra, finalmente, que no siempre se gana y creo que la parte superior de resumen de esta respuesta es engañosa

  3. 9

    De acuerdo a la Documentación de Python:

    No importa lo que pasó anteriormente, el último bloque se ejecuta una vez que el bloque de código es completa y cualquier planteado excepciones se manejan. Incluso si hay un error en un controlador de excepción o de la otra cuadra y una nueva excepción, el código en el final de bloque es todavía funcionan.

    También se debe señalar que si hay varias instrucciones return, incluyendo uno en el bloque finally, a continuación, el bloque finally retorno es el único que se va a ejecutar.

  4. 8

    Bueno, sí y no.

    Lo que está garantizado es que en Python siempre tratamos de ejecutar el bloque finally. En el caso de que el retorno de la cuadra o elevar una excepción no capturada, el bloque finally se ejecuta justo antes de devolver o mejorar la excepción.

    (lo que podría haber controlado a ti mismo simplemente ejecutando el código en tu pregunta)

    El único caso que puedo imaginar de donde el bloque finally no será ejecutada cuando el Python interpretor sí se bloquea, por ejemplo, en el código de C o debido a la interrupción de la alimentación.

    • ja, ja, .. o hay un bucle infinito en el try catch
    • Es uncatched o no detectada?
    • Fijo. Perdón por el error, pero el inglés no es mi lengua materna…
    • No hay problema. Al menos aquí en las zonas rurales de reino unido un montón de gente decir «atrapados». Los nativos también.
    • Pienso «Bueno, sí y no» es la más correcta. por último: siempre gana, donde «siempre» significa que el intérprete es capaz de ejecutar y el código para el «por último:» está disponible todavía, y «gana» se define como el intérprete se intente ejecutar el finalmente: bloque y tendrá éxito. Ese es el «Sí» y es muy condicional. «No» es la manera en la que el intérprete puede detener antes de que «finalmente:»- fallo de alimentación, fallo de hardware, kill -9 destinadas a la intérprete, errores en el intérprete o el código depende, otras maneras de colgar el intérprete. Y formas para colgar en la «finalmente:».
  5. 0

    Para entender realmente cómo funciona, sólo tiene que ejecutar estos dos ejemplos:

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

      de salida será de

      finalmente

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

      de salida será de

      excepto

      finalmente

  6. 0

    Creo que este es uno sin el uso de un generador de función:

    import multiprocessing
    import time
    
    def fun(arg):
      try:
        print("tried " + str(arg))
        time.sleep(arg)
      finally:
        print("finally cleaned up " + str(arg))
      return foo
    
    list = [1, 2, 3]
    multiprocessing.Pool().map(fun, list)

    El sueño puede ser cualquier código que puede ejecutar para inconsistente cantidades de tiempo.

    Lo que parece estar sucediendo es que el primer proceso en paralelo a final deja el bloque try correctamente, pero los intentos para que la función devuelva un valor (foo) que no se ha definido en cualquier lugar, lo que provoca una excepción. Con excepción de la mata a la mapa, sin permitir que los demás procesos para alcanzar sus bloques finally.

    Además, si se agrega la línea de bar = bazz justo después de la llamada sleep() en el bloque try. A continuación, el primer proceso para llegar a la línea que lanza una excepción (porque bazz no está definido), que hace que su propio bloque finally se ejecuta, pero luego mata a la mapa, hace que los otros bloques try a desaparecer sin llegar sus bloques finally, y el primer proceso de no llegar a su instrucción return, ya sea.

    Lo que esto significa para Python multiprocesamiento es que usted no puede confiar en el mecanismo de excepciones para limpiar los recursos en todos los procesos, incluso si uno de los procesos puede tener una excepción. Señal adicional de manejo o la gestión de los recursos fuera de la multiprocesamiento mapa de la llamada sería necesario.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea