Por que precisamos da cláusula "finalmente" em Python?

306

Não sei por que precisamos finallyde try...except...finallydeclarações. Na minha opinião, esse bloco de código

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

é o mesmo com este usando finally:

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

Estou esquecendo de algo?

RNA
fonte

Respostas:

422

Faz diferença se você voltar cedo:

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

Compare com isso:

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

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

Outras situações que podem causar diferenças:

  • Se uma exceção for lançada dentro do bloco de exceção.
  • Se uma exceção é lançada, run_code1()mas não é umaTypeError .
  • Outras declarações de fluxo de controle, como continuee break.
Mark Byers
fonte
1
tente: #x = Olá + 20 x = 10 + 20, exceto: print 'Estou dentro do bloco' x = 20 + 30 else: print 'Estou no outro bloco' x + = 1 finalmente: print 'Finalmente x =% s '% (x)
Abhijit Sahu 02/10
89

Você pode usar finallypara garantir que arquivos ou recursos sejam fechados ou liberados, independentemente de ocorrer uma exceção, mesmo que você não a capture. (Ou se você não capturar essa exceção 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

Neste exemplo, seria melhor usar o método with instrução, mas esse tipo de estrutura pode ser usado para outros tipos de recursos.

Alguns anos depois, escrevi uma postagem no blog sobre um abuso finallyque os leitores podem achar divertido.

kindall
fonte
23

Eles não são equivalentes. Finalmente, o código é executado, não importa o que mais aconteça. É útil para códigos de limpeza que precisam ser executados.

Antimônio
fonte
15
Finally code is run no matter what else happens... a menos que haja um loop infinito. Ou um powercut. Or os._exit(). Ou ...
Mark Byers
3
@ Mark Na verdade, o sys.exit lança uma exceção normal. Mas sim, qualquer coisa que faça com que o processo seja encerrado imediatamente significa que nada mais será executado.
Antimony
1
@ Antimony: Obrigado. Alterado para os._exit.
22812 Mark Byers
Apenas imaginando, por que o código de limpeza não pode ser colocado dentro de, exceto se o código for inserido, exceto apenas se uma exceção for encontrada?
Stephen Jacob
2
@ Stephen Por um lado, finalmente o código é executado mesmo se você retornar do bloco try. Nesse caso, você não atingirá a cláusula exceto.
Antimony
18

Para adicionar às outras respostas acima, a finallycláusula é executada independentemente do que a elsecláusula seja executada apenas se uma exceção não tiver sido gerada.

Por exemplo, a gravação em um arquivo sem exceções produzirá o seguinte:

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.")

RESULTADO:

Writing to file.
Write successful.
File closed.

Se houver uma exceção, o código produzirá o seguinte (observe que um erro deliberado é causado pela manutenção do arquivo somente leitura.

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.")

RESULTADO:

Could not write to file.
File closed.

Podemos ver que a finallycláusula é executada independentemente de uma exceção. Espero que isto ajude.

capitão preto
fonte
2
Isso funcionaria mesmo se você não usasse a cláusula "finally", que não responde à pergunta, já que o OP quer saber a diferença. Um bom exemplo estaria causando um erro diferente do IOError, para mostrar que o Finalmente, o bloco de cláusulas é executado antes da propagação da exceção para o chamador.
Reda Drissi 7/03/19
2
Eu não sabia que elseera uma coisa. Útil saber.
mazunki
8

Os blocos de código não são equivalentes. ofinally cláusula também será executada se run_code1()lançar uma exceção diferente de TypeError, ou se run_code2()lançar uma exceção, enquanto other_code()na primeira versão não seria executada nesses casos.

Sven Marnach
fonte
7

No seu primeiro exemplo, o que acontece se run_code1()gera uma exceção que não é TypeError? ...other_code() não será executado.

Compare isso com a finally:versão: other_code()é garantido que seja executado, independentemente de qualquer exceção ser levantada.

mhawke
fonte
7

Conforme explicado na documentação , a finallycláusula destina-se a definir ações de limpeza que devem ser executadas em todas as circunstâncias .

Se finallypresente, ele especifica um manipulador de 'limpeza'. A try cláusula é executada, incluindo as cláusulas any excepte else. Se uma exceção ocorrer em qualquer uma das cláusulas e não for tratada, a exceção será salva temporariamente. A finallycláusula é executada. Se houver uma exceção salva, ela será gerada novamente no final da finally cláusula.

Um exemplo:

>>> 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 você pode ver, a finallycláusula é executada em qualquer evento. O TypeErrorgerado pela divisão de duas seqüências de caracteres não é tratado pela exceptcláusula e, portanto, aumentado novamente após a finallyexecução da cláusula.

Em aplicativos do mundo real, a cláusula Finalmente é útil para liberar recursos externos (como arquivos ou conexões de rede), independentemente de o uso do recurso ter sido bem-sucedido.

Eugene Yarmash
fonte
4

Exemplo perfeito é como abaixo:

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
fonte
3

finallyé para definir "ações de limpeza" . A finallycláusula é executada em qualquer caso antes de sair dotry instrução, independentemente de uma exceção (mesmo que você não a manipule).

Eu segundo exemplo de @ Byers.

kakhkAtion
fonte
2

Finalmente, também pode ser usado quando você deseja executar o código "opcional" antes de executar o código para o seu trabalho principal e esse código opcional pode falhar por vários motivos.

No exemplo a seguir, não sabemos exatamente que tipo de exceção store_some_debug_infopode gerar.

Nós poderíamos correr:

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

Mas, a maioria dos linters se queixam de pegar uma exceção muito vaga. Além disso, como optamos apenas passpor erros, o exceptbloco realmente não agrega valor.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

O código acima tem o mesmo efeito que o primeiro bloco de código, mas é mais conciso.

Brad Johnson
fonte
2

Usar o delphi profissionalmente por alguns anos me ensinou a proteger minhas rotinas de limpeza usando finalmente. O Delphi reforça o uso de finalmente para limpar todos os recursos criados antes do bloco try, para que você não cause um vazamento de memória. É assim também que Java, Python e Ruby funcionam.

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

e os recursos serão limpos, independentemente do que você faça entre tentar e finalmente. Além disso, não será limpo se a execução nunca atingir o trybloco. (isto é create_resource, lança uma exceção) Torna seu código "exceção segura".

Quanto ao motivo pelo qual você realmente precisa de um bloco finalmente, nem todos os idiomas precisam. No C ++, você chama automaticamente destruidores que impõem a limpeza quando uma exceção desenrola a pilha. Eu acho que este é um passo na direção de um código mais limpo comparado a tentar ... finalmente idiomas.

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

Um bloco try possui apenas uma cláusula obrigatória: A instrução try. As cláusulas exceto, else e finalmente são opcionais e baseadas na preferência do usuário.

finalmente: Antes de o Python deixar a instrução try, ele executará o código no bloco final sob quaisquer condições, mesmo que esteja finalizando o programa. Por exemplo, se o Python encontrou um erro ao executar o código no bloco exceto ou então, o bloco finalmente ainda será executado antes de parar o programa.

Lawrence Krukrubo
fonte
1
Isto está errado. A declaração de exceção é obrigatória. - Lucas Azevedo 1 de fevereiro às 12:04 Isso está errado, pois acabei de compilar e executar um programa Python 3.5 com um bloco try-finally, sem a cláusula "exceto".
Rob Tow
2
Eu mesmo tentei isso e, para minha descrença, a cláusula exceto não é obrigatória.
captainblack
1

Execute estes códigos Python3 para observar a necessidade 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))

CASO2:

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))

Tente as seguintes entradas sempre:

  1. números inteiros aleatórios
  2. código correto que é 586 (tente isso e você receberá sua resposta)
  3. cordas aleatórias

** Em um estágio muito inicial do aprendizado de Python.

AshPython
fonte
1

Eu estava tentando executar um código onde eu queria ler folhas de excel. O problema era que, se houver um arquivo que não possui uma planilha com o nome digamos: SheetSum Não consigo movê-lo para o local do erro !! O código que escrevi foi:

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')

Erro:

[WinError 32] O processo não pode acessar o arquivo porque está sendo usado por outro processo

Eu tive que adicionar um try except with finallybloco completo e dizer que finallyeu preciso fechar o arquivo em qualquer 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')

Caso contrário, o arquivo ainda permanece aberto é o plano de fundo.

Se finallypresente, ele especifica um manipulador de limpeza . A try cláusula é executada, incluindo as cláusulas any excepte else. Se uma exceção ocorrer em qualquer uma das cláusulas e não for tratada, a exceção será salva temporariamente . A finallycláusula é executada. Se houver uma exceção salva, ela será gerada novamente no final da finally cláusula. Se a finallycláusula gerar outra exceção, a exceção salva será configurada como o contexto da nova exceção.

..Mais Aqui

Saqib Mujtaba
fonte