¿Por qué necesitamos la cláusula "finally" en Python?


No estoy seguro de por qué necesitamos {[2] {} en[3]} declaraciones. En mi opinión, este bloque de código

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

Es lo mismo con este usando finally:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

¿Me estoy perdiendo algo?

Author: Eugene Yarmash, 2012-07-19

11 answers

Hace una diferencia si regresa temprano:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

Compárese con esto:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

Otras situaciones que pueden causar diferencias:

  • Si se lanza una excepción dentro del bloque except.
  • Si se lanza una excepción en run_code1() pero no es un TypeError.
  • Otras declaraciones de flujo de control como continue y break.
 245
Author: Mark Byers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-19 00:01:10

Puede usar finally para asegurarse de que los archivos o recursos estén cerrados o liberados independientemente de si se produce una excepción, incluso si no se detecta la excepción. (O si no captas esa excepción específica.)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

En este ejemplo sería mejor usar la instrucción with, pero este tipo de estructura se puede usar para otros tipos de recursos.

Unos años más tarde, escribí una entrada de blog sobre un abuso de finally que los lectores pueden encontrar divertido.

 46
Author: kindall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-28 18:49:37

No son equivalentes. Finalmente el código se ejecuta sin importar qué más suceda. Es útil para el código de limpieza que tiene que ejecutarse.

 15
Author: Antimony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-18 23:46:12

Los bloques de código no son equivalentes. La cláusula finally también se ejecutará si run_code1() lanza una excepción que no sea TypeError, o si run_code2() lanza una excepción, mientras que other_code() en la primera versión no se ejecutaría en estos casos.

 7
Author: Sven Marnach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-18 23:46:36

En su primer ejemplo, ¿qué sucede si run_code1() plantea una excepción que no es TypeError? ... other_code() no se ejecutará.

Compare eso con la versión finally:: other_code() se garantiza que se ejecutará independientemente de cualquier excepción que se genere.

 6
Author: mhawke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-18 23:47:41

Para agregar a las otras respuestas anteriores, la cláusula finally se ejecuta sin importar qué mientras que la cláusula else se ejecuta solo si no se planteó una excepción.

Por ejemplo, escribir en un archivo sin excepciones generará lo siguiente:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

SALIDA:

Writing to file.
Write successful.
File closed.

Si hay una excepción, el código generará lo siguiente (tenga en cuenta que un error deliberado es causado por mantener el archivo solo lectura.

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

SALIDA:

Could not write to file.
File closed.

Podemos ver que la cláusula finally se ejecuta independientemente de una excepción. Espero que esto ayude.

 5
Author: captainblack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-13 06:46:06

finally es para definir "acciones de limpieza". La cláusula finally se ejecuta en cualquier caso antes de salir de la instrucción try, ya sea que se haya producido o no una excepción (incluso si no la maneja).

Secundo el ejemplo de @Byers.

 3
Author: kakhkAtion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-15 17:59:29

Finalmente también se puede usar cuando se desea ejecutar código "opcional" antes de ejecutar el código para su trabajo principal y ese código opcional puede fallar por varias razones.

En el siguiente ejemplo, no sabemos exactamente qué tipo de excepciones store_some_debug_info podría generar.

Podríamos ejecutar:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

Pero, la mayoría de los linters se quejan de la captura demasiado vaga de una excepción. Además, dado que elegimos solo pass para errores, el bloque except realmente no agrega valor.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

El código anterior tiene el mismo efecto que el 1er bloque de código, pero es más conciso.

 2
Author: Brad Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-17 17:14:46

El ejemplo perfecto es el siguiente:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)
 2
Author: Abhijit Sahu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-02 06:33:36

Como se explica en la documentación , la cláusula finally tiene por objeto definir las acciones de limpieza que deben ejecutarse en todas las circunstancias.

Si finally está presente, especifica un controlador 'cleanup'. El try se ejecuta la cláusula, incluyendo las cláusulas except y else. Si un excepción se produce en cualquiera de las cláusulas y no se maneja, el la excepción se guarda temporalmente. Se ejecuta la cláusula finally. Si hay una excepción guardada que es al final de la finally clausula.

Un ejemplo:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Como puede ver, la cláusula finally se ejecuta en cualquier caso. La TypeError elevada dividiendo dos cadenas no es manejada por la cláusula except y por lo tanto re-elevada después de que la cláusula finally haya sido ejecutada.

En aplicaciones del mundo real, la cláusula finally es útil para liberar recursos externos (como archivos o conexiones de red), independientemente de si el uso del recurso fue exitoso.

 2
Author: Eugene Yarmash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-03 13:20:56

Usar delphi profesionalmente durante algunos años me enseñó a salvaguardar mis rutinas de limpieza usando finalmente. Delphi prácticamente impone el uso de finally para limpiar cualquier recurso creado antes del bloque try, para que no cause una pérdida de memoria. Así también funciona Java, Python y Ruby.

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

Y el recurso se limpiará independientemente de lo que hagas entre el intento y finalmente. Además, no se limpiará si la ejecución nunca llega al bloque try. (es decir, create_resource se lanza una excepción) Hace que su código "excepción segura".

En cuanto a por qué realmente necesita un bloque final, no todos los idiomas lo hacen. En C++, donde ha llamado automáticamente destructores que imponen la limpieza cuando una excepción desenrolla la pila. Creo que este es un paso adelante en la dirección de un código más limpio en comparación con try...finalmente idiomas.

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.
 1
Author: nurettin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-21 21:43:16