Para cualquier posible bloque try-finally en Python, ¿se garantiza que el finally
bloque siempre se ejecutará?
Por ejemplo, digamos que regreso mientras estoy en un except
bloque:
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 finally
se 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 finally
bloque no pueda ejecutarse en Python?
python
exception-handling
try-catch-finally
finally
Stevoisiak
fuente
fuente
finally
no se ejecuta o "vence su propósito" es durante un bucle infinitosys.exit
o una interrupción forzada. La documentación indica quefinally
siempre se ejecuta, así que iría con eso.finally
no se ejecutará. O lo mismo si la computadora falla antes: Dfinally
no 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
finally
merece. Lo que está garantizado es que si la ejecución fluye fuera del conjuntotry
-finally
construcción, pasará por elfinally
para hacerlo. Lo que no está garantizado es que la ejecución fluirá fuera detry
-finally
.Es
finally
posible 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
finally
bloque lanzando unaGeneratorExit
excepción, pero aquí capturamos esa excepción y luegoyield
nuevamente, 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 with
await
s__aexit__
, o si el objetoawait
s oyield
s en unfinally
bloque. Esta lista no pretende ser exhaustiva.A
finally
en un hilo de daemon podría nunca ejecutarse si todos los hilos no daemon salen primero.os._exit
detendrá el proceso inmediatamente sin ejecutarfinally
bloques.os.fork
puede hacer que losfinally
bloques 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
multiprocessing
utiliza 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._exit
al trabajador una vez que se realiza el trabajo del trabajador,finally
y lamultiprocessing
interacción puede ser problemática ( ejemplo ).finally
bloques se ejecuten.kill -SIGKILL
evitará que losfinally
bloques se ejecuten.SIGTERM
ySIGHUP
también evitará que losfinally
bloques se ejecuten a menos que instale un controlador para controlar el apagado usted mismo; por defecto, Python no manejaSIGTERM
oSIGHUP
.finally
puede evitar que se complete la limpieza. Un caso particularmente notable es si el usuario toca control-C justo cuando estamos comenzando a ejecutar elfinally
bloque. Python elevará ayKeyboardInterrupt
omitirá cada línea delfinally
contenido del bloque. (KeyboardInterrupt
El código seguro es muy difícil de escribir).finally
bloques no se ejecutarán.El
finally
bloque 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 dependerfinally
demasiado.fuente
except
y nunca se metaGeneratorExit
dentro de un generador. Se esperan los puntos sobre hilos / matar el proceso / segfaulting / apagado, python no puede hacer magia. Además: las excepcionesfinally
son obviamente un problema, pero esto no cambia el hecho de que el flujo de control se movió alfinally
bloque. 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 -9
no 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.finally
bloques 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 accidentalmenteGeneratorExit
incluso si no atrapa elGeneratorExit
, por ejemplo, si seasync with
suspende 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
finally
en 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._exit
es, 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 = bazz
justo 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