Para cualquier posible bloque try-finally en Python, ¿se garantiza que el finallybloque siempre se ejecutará?
Por ejemplo, digamos que regreso mientras estoy en un exceptbloque:
try:
1/0
except ZeroDivisionError:
return
finally:
print("Does this code run?")
O tal vez vuelvo a subir un Exception:
try:
1/0
except ZeroDivisionError:
raise
finally:
print("What about this code?")
Las pruebas muestran que finallyse ejecuta para los ejemplos anteriores, pero imagino que hay otros escenarios en los que no he pensado.
¿Hay algún escenario en el que un finallybloque no pueda ejecutarse en Python?
python
exception-handling
try-catch-finally
finally
Stevoisiak
fuente
fuente

finallyno se ejecuta o "vence su propósito" es durante un bucle infinitosys.exito una interrupción forzada. La documentación indica quefinallysiempre se ejecuta, así que iría con eso.finallyno se ejecutará. O lo mismo si la computadora falla antes: Dfinallyno se ejecutará si el cable de alimentación se arranca de la pared.Respuestas:
"Garantizado" es una palabra mucho más fuerte que cualquier implementación de
finallymerece. Lo que está garantizado es que si la ejecución fluye fuera del conjuntotry-finallyconstrucción, pasará por elfinallypara hacerlo. Lo que no está garantizado es que la ejecución fluirá fuera detry-finally.Es
finallyposible que nunca se ejecute A en un generador o en una rutina asíncrona , si el objeto nunca se ejecuta hasta la conclusión. Hay muchas maneras en que podría suceder; Aquí hay uno:Tenga en cuenta que este ejemplo es un poco complicado: cuando el generador se recolecta basura, Python intenta ejecutar el
finallybloque lanzando unaGeneratorExitexcepción, pero aquí capturamos esa excepción y luegoyieldnuevamente, en ese momento Python imprime una advertencia ("generador ignorado Generator Salir ") y se da por vencido. Ver PEP 342 (Corutinas a través de generadores mejorados) para más detalles.Otras formas en que un generador o una rutina no pueden ejecutarse hasta la conclusión incluyen si el objeto nunca se GC'ed (sí, eso es posible, incluso en CPython), o si una
async withawaits__aexit__, o si el objetoawaits oyields en unfinallybloque. Esta lista no pretende ser exhaustiva.A
finallyen un hilo de daemon podría nunca ejecutarse si todos los hilos no daemon salen primero.os._exitdetendrá el proceso inmediatamente sin ejecutarfinallybloques.os.forkpuede hacer que losfinallybloques se ejecuten dos veces . Además de los problemas normales que esperaría de las cosas que suceden dos veces, esto podría causar conflictos de acceso concurrentes (bloqueos, paradas, ...) si el acceso a los recursos compartidos no se sincroniza correctamente .Dado que
multiprocessingutiliza fork-without-exec para crear procesos de trabajo cuando se usa el método de inicio de fork (el valor predeterminado en Unix), y luego llamaos._exital trabajador una vez que se realiza el trabajo del trabajador,finallyy lamultiprocessinginteracción puede ser problemática ( ejemplo ).finallybloques se ejecuten.kill -SIGKILLevitará que losfinallybloques se ejecuten.SIGTERMySIGHUPtambién evitará que losfinallybloques se ejecuten a menos que instale un controlador para controlar el apagado usted mismo; por defecto, Python no manejaSIGTERMoSIGHUP.finallypuede evitar que se complete la limpieza. Un caso particularmente notable es si el usuario toca control-C justo cuando estamos comenzando a ejecutar elfinallybloque. Python elevará ayKeyboardInterruptomitirá cada línea delfinallycontenido del bloque. (KeyboardInterruptEl código seguro es muy difícil de escribir).finallybloques no se ejecutarán.El
finallybloque no es un sistema de transacción; no proporciona garantías de atomicidad ni nada por el estilo. Algunos de estos ejemplos pueden parecer obvios, pero es fácil olvidar que tales cosas pueden suceder y dependerfinallydemasiado.fuente
excepty nunca se metaGeneratorExitdentro de un generador. Se esperan los puntos sobre hilos / matar el proceso / segfaulting / apagado, python no puede hacer magia. Además: las excepcionesfinallyson obviamente un problema, pero esto no cambia el hecho de que el flujo de control se movió alfinallybloque. Con respecto a estoCtrl+C, puede agregar un controlador de señal que lo ignore, o simplemente "programar" un apagado limpio después de que se complete la operación actual.kill -9no especificó un idioma. Y, francamente, necesita repetirse, porque se encuentra en un punto ciego. Demasiadas personas olvidan, o no se dan cuenta, de que su programa podría detenerse en seco sin siquiera permitirse la limpieza.finallybloques como si proporcionaran garantías transaccionales. Puede parecer obvio que no lo hacen, pero no es algo que todos se den cuenta. En cuanto al caso del generador, hay muchas formas en que un generador no puede ser GC'ed en absoluto, y muchas formas en que un generador o una rutina pueden ceder accidentalmenteGeneratorExitincluso si no atrapa elGeneratorExit, por ejemplo, si seasync withsuspende una co-rutina en__exit__.Si. Finalmente siempre gana.
La única forma de derrotarlo es detener la ejecución antes de tener la
finally:oportunidad de ejecutarla (por ejemplo, bloquear el intérprete, apagar la computadora, suspender un generador para siempre).Aquí hay un par más que quizás no haya pensado:
Dependiendo de cómo salga del intérprete, a veces puede "cancelar" finalmente, pero no así:
Usando lo precario
os._exit(en mi opinión, esto se clasifica como "accidente del intérprete"):Actualmente estoy ejecutando este código, para probar si finalmente aún se ejecutará después de la muerte por calor del universo:
Sin embargo, todavía estoy esperando el resultado, así que vuelve aquí más tarde.
fuente
finallyen un generador o en una rutina puede fallar fácilmente en la ejecución , sin acercarse a una condición de "bloqueo del intérprete".sleep(1)lo que definitivamente daría lugar a un comportamiento indefinido. :-Dos._exites, a todos los efectos prácticos, lo mismo que inducir un bloqueo (salida impura). La forma correcta de salir essys.exit.De acuerdo con la documentación de Python :
También debe tenerse en cuenta que si hay varias declaraciones de retorno, incluida una en el bloque de finalmente, entonces el retorno de último bloque es el único que se ejecutará.
fuente
Pues sí y no.
Lo que está garantizado es que Python siempre intentará ejecutar el último bloque. En el caso de que regrese del bloque o genere una excepción no capturada, el bloque finalmente se ejecuta justo antes de regresar o aumentar la excepción.
(lo que podría haber controlado usted mismo simplemente ejecutando el código en su pregunta)
El único caso que puedo imaginar donde el bloque finalmente no se ejecutará es cuando el propio intérprete de Python se bloquea, por ejemplo, dentro del código C o debido a un corte de energía.
fuente
Encontré este sin usar una función de generador:
La suspensión puede ser cualquier código que pueda ejecutarse durante períodos de tiempo inconsistentes.
Lo que parece estar sucediendo aquí es que el primer proceso paralelo que termina deja el bloque try con éxito, pero luego intenta devolver de la función un valor (foo) que no se ha definido en ninguna parte, lo que causa una excepción. Esa excepción mata el mapa sin permitir que los otros procesos alcancen finalmente sus bloques.
Además, si agrega la línea
bar = bazzjusto después de la llamada sleep () en el bloque try. Luego, el primer proceso para llegar a esa línea arroja una excepción (porque el bazz no está definido), lo que hace que se ejecute su propio bloque finalmente, pero luego mata el mapa, haciendo que los otros bloques de prueba desaparezcan sin llegar a sus bloques finalmente, y el primer proceso tampoco llega a su declaración de devolución.Lo que esto significa para el multiprocesamiento de Python es que no puede confiar en el mecanismo de manejo de excepciones para limpiar recursos en todos los procesos si incluso uno de los procesos puede tener una excepción. Sería necesario un manejo adicional de la señal o la administración de los recursos fuera de la llamada del mapa de multiprocesamiento.
fuente
Anexo a la respuesta aceptada, solo para ayudar a ver cómo funciona, con algunos ejemplos:
Esta:
saldrá
saldrá
fuente