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

306

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

ARN
fuente

Respuestas:

422

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 a 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 run_code1()pero no es un TypeError.
  • Otras declaraciones de flujo de control como continuey breakdeclaraciones.
Mark Byers
fuente
1
intente: #x = Hola + 20 x = 10 + 20 excepto: imprima 'Estoy dentro excepto el bloque' x = 20 + 30 más: imprima 'Estoy en el bloque más' x + = 1 finalmente: imprima 'Finalmente x =% s '% (x)
Abhijit Sahu
89

Puede usar finallypara asegurarse de que los archivos o recursos estén cerrados o liberados, independientemente de si se produce una excepción, incluso si no 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 withdeclaración, pero este tipo de estructura se puede usar para otros tipos de recursos.

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

un poco
fuente
23

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

Antimonio
fuente
15
Finally code is run no matter what else happens... a menos que haya un bucle infinito. O un corte de energía. O os._exit(). O ...
Mark Byers
3
@Mark En realidad, sys.exit produce una excepción normal. Pero sí, cualquier cosa que haga que el proceso finalice inmediatamente significará que nada más se ejecuta.
Antimonio
1
@Antimony: Gracias. Cambiado a os._exit.
Mark Byers
Solo me pregunto, ¿por qué no se puede colocar el código de limpieza dentro de, excepto si el código ingresará, excepto si se encuentra una excepción?
Stephen Jacob
2
@Stephen Por un lado, finalmente el código se ejecuta incluso si regresa del bloque try. En ese caso, no golpeará la cláusula except.
Antimonio
18

Para agregar a las otras respuestas anteriores, la finallycláusula se ejecuta sin importar qué mientras que la elsecláusula se ejecuta solo si no se generó 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 se produce un error deliberado 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 finallycláusula se ejecuta independientemente de una excepción. Espero que esto ayude.

capitán negro
fuente
2
Esto hubiera funcionado incluso si no hubiera utilizado la cláusula "finalmente" que no responde a la pregunta, ya que OP quiere saber la diferencia, un buen ejemplo habría estado causando un error diferente al de IOError, para mostrar que el Finalmente, el bloque de cláusulas se ejecuta antes de que la excepción se propague a la persona que llama.
Reda Drissi
2
No sabía que elseera una cosa. Útil para saber.
mazunki
8

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

Sven Marnach
fuente
7

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

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

mhawke
fuente
7

Como se explica en la documentación , la finallycláusula está destinada a definir acciones de limpieza que deben ejecutarse en todas las circunstancias .

Si finallyestá presente, especifica un controlador de "limpieza". La try cláusula se ejecuta, incluidas las cláusulas excepty else. Si se produce una excepción en cualquiera de las cláusulas y no se maneja, la excepción se guarda temporalmente. La finallycláusula se ejecuta. Si hay una excepción guardada, se vuelve a generar al final de la finally cláusula.

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 finallycláusula se ejecuta en cualquier caso. La cláusula TypeErrorno maneja el aumento dividiendo dos cadenas excepty, por lo tanto, se vuelve a generar después de que la finallycláusula se haya ejecutado.

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

Eugene Yarmash
fuente
4

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)
Abhijit Sahu
fuente
3

finallyes para definir "acciones de limpieza" . La finallycláusula se ejecuta en cualquier caso antes de abandonar la trydeclaración, ya sea que haya ocurrido una excepción (incluso si no la maneja) o no.

Segundo el ejemplo de @ Byers.

kakhkAtion
fuente
2

Finalmente, también se puede usar cuando 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 con precisión qué tipo de excepciones store_some_debug_infopodría arrojar.

Podríamos correr:

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

Pero, la mayoría de los linters se quejarán por la captura de una excepción demasiado vaga. Además, dado que estamos eligiendo solo passpor errores, el exceptbloque 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.

Brad Johnson
fuente
2

Usar Delphi profesionalmente durante algunos años me enseñó a salvaguardar mis rutinas de limpieza usando finalmente. Delphi prácticamente aplica el uso de finalmente para limpiar los recursos creados antes del bloque try, para evitar 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 haga entre el intento y el final. Además, no se limpiará si la ejecución nunca llega al trybloque. (es decir, create_resourcesí mismo arroja una excepción) Hace que su código "excepción segura".

En cuanto a por qué realmente necesita un bloqueo 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 probar ... finalmente los idiomas.

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

Un bloque try tiene solo una cláusula obligatoria: la declaración try. Las cláusulas except, else y finally son opcionales y se basan en la preferencia del usuario.

finalmente: antes de que Python abandone la declaración de prueba, ejecutará el código en el bloque finalmente bajo cualquier condición, incluso si está terminando el programa. Por ejemplo, si Python se encontró con un error al ejecutar el código en el bloque except o else, el bloque finalmente todavía se ejecutará antes de detener el programa.

Lawrence Krukrubo
fuente
1
Esto está mal. La declaración de excepción es obligatoria. - Lucas Azevedo 1 de febrero a las 12:04 Esto está mal, ya que acabo de compilar y ejecutar un programa Python 3.5 con un bloque try-finally, sin cláusula "excepto".
Rob Tow
2
Lo intenté yo mismo y para mi incredulidad, la cláusula de excepción no es obligatoria.
captainblack
1

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 al azar
  2. código correcto que es 586 (intente esto y obtendrá su respuesta)
  3. cadenas al azar

** En una etapa muy temprana de aprendizaje de Python.

AshPython
fuente
1

Estaba tratando de ejecutar un código donde quería leer las hojas de Excel. El problema era, si hay un archivo que no tiene una hoja llamada, por ejemplo: SheetSum ¡No puedo moverlo a la ubicación de error! El código que escribí fue:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Dando error:

[WinError 32] El proceso no puede acceder al archivo porque otro proceso lo está utilizando

Tuve que agregar el try except with finallybloque completo y decir que finallynecesito cerrar el archivo en cualquier caso como:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

De lo contrario, el archivo sigue abierto es el fondo.

Si finallyestá presente, especifica un controlador de limpieza . La try cláusula se ejecuta, incluidas las cláusulas excepty else. Si se produce una excepción en cualquiera de las cláusulas y no se maneja, la excepción se guarda temporalmente . La finallycláusula se ejecuta. Si hay una excepción guardada, se vuelve a generar al final de la finally cláusula. Si la finallycláusula genera otra excepción, la excepción guardada se establece como el contexto de la nueva excepción.

..Más Aquí

Saqib Mujtaba
fuente