¿Por qué necesitamos la cláusula “finalmente” en Python?

No estoy seguro de por qué necesitamos finally try...except...finally 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 de algo?

Hace una diferencia si regresas temprano:

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

Compare 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 excepto.
  • Si se lanza una excepción en run_code1() pero no es un TypeError .
  • Otras declaraciones de flujo de control, tales como continue y break declaraciones.

finally puede usar para asegurarse de que los archivos o recursos se cierren o liberen independientemente de si se produce una excepción, incluso si no se detecta la excepción. (O si no atrapa 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 utilizar la instrucción with , pero este tipo de estructura puede usarse para otros tipos de recursos.

Unos años más tarde, escribí una publicación en el blog sobre un abuso que finally lectores pueden encontrar divertido.

No son equivalentes. Finalmente el código se ejecuta sin importar lo que suceda. Es útil para el código de limpieza que debe ejecutarse.

Para agregar a las otras respuestas anteriores, la cláusula finally ejecuta sin importar qué, mientras que la cláusula else ejecuta solo si no se produce 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 se debe al mantener el archivo de 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 ejecuta independientemente de una excepción. Espero que esto ayude.

Los bloques de código no son equivalentes. La cláusula finally también se ejecutará si run_code1() lanza una excepción diferente a 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.

En su primer ejemplo, ¿qué sucede si run_code1() genera una excepción que no es TypeError ? … other_code() no será ejecutado.

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

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

Yo segundo el ejemplo de @ Byers.

Finalmente, también puede usarse cuando desea ejecutar el 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 con precisión qué tipo de excepciones podría lanzar store_some_debug_info .

Podríamos correr:

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

Sin embargo, la mayoría de los linters se quejarán de una excepción demasiado vaga. Además, dado que estamos eligiendo pass solo por errores, el bloque de 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 primer bloque de código, pero es más conciso.

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) 

Como se explica en la documentación , la cláusula finally está destinada a definir acciones de limpieza que deben ejecutarse en cualquier circunstancia .

Si finally está presente, especifica un controlador de “limpieza”. Se ejecuta la cláusula try , incluidas las cláusulas except y else . Si ocurre una excepción en cualquiera de las cláusulas y no se maneja, la excepción se guarda temporalmente. La finally cláusula se ejecuta. Si hay una excepción guardada, se vuelve a elevar al final de la cláusula finally .

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 "", line 1, in  File "", 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. El TypeError generado al dividir dos cadenas no se maneja con la cláusula de except y, por lo tanto, se vuelve a elevar después de que se haya ejecutado la finally cláusula.

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.

El uso profesional de Delphi durante algunos años me enseñó a proteger mis rutinas de limpieza utilizando finalmente. Delphi prácticamente impone el uso de finalmente para limpiar los recursos creados antes del bloque try, para que no se produzca una pérdida de memoria. Así es como funciona Java, Python y Ruby.

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

y el recurso se limpiará independientemente de lo que haga entre intentar y finalmente. Además, no se limpiará si la ejecución nunca llega al bloque try . (es decir, create_resource lanza una excepción) Hace que su código sea “seguro para las excepciones”.

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

 { type object1; smart_pointer object1(new type()); } // destructors are automagically called here in LIFO order so no finally required. 

Un bloque de prueba tiene solo una cláusula obligatoria: la statement de prueba. Las cláusulas except, else y finalmente son opcionales y se basan en las preferencias del usuario.

Finalmente: antes de que Python abandone la instrucción try, ejecutará el código en el bloque finally bajo cualquier condición, incluso si está finalizando el progtwig. Por ejemplo, si Python se encontró con un error al ejecutar el código en el bloque de excepción o de lo contrario, el bloque finally todavía se ejecutará antes de detener el progtwig.

Ejecute estos códigos Python3 para ver la necesidad de finalmente:

CASO 1:

 count = 0 while True: count += 1 if count > 3: break else: try: x = int(input("Enter your lock number here: ")) if x == 586: print("Your lock has unlocked :)") break else: print("Try again!!") continue except: print("Invalid entry!!") finally: print("Your Attempts: {}".format(count)) 

CASO 2:

 count = 0 while True: count += 1 if count > 3: break else: try: x = int(input("Enter your lock number here: ")) if x == 586: print("Your lock has unlocked :)") break else: print("Try again!!") continue except: print("Invalid entry!!") print("Your Attempts: {}".format(count)) 

Pruebe las siguientes entradas cada vez:

  1. enteros aleatorios
  2. código correcto, que es 586 (Prueba esto y obtendrás tu respuesta)
  3. cadenas aleatorias

** En una etapa muy temprana de aprender Python.