Curso de Python / Tratar exceções com try, except, else, finally e raise

O tratamento de exceções permite reagir a falhas esperadas ou informar o problema a quem chamou uma função. Mantenha o bloco try focado na operação que pode falhar e capture somente os erros para os quais existe uma ação definida; esconder qualquer exceção pode deixar o programa continuar num estado incorreto.

Entender os caminhos de try, except, else e finally

O bloco try contém a operação monitorada. Se uma exceção ocorrer ali, as instruções restantes desse bloco são puladas e o Python procura um except compatível. Quando encontra, executa o tratamento; se não encontra, a exceção continua para um contexto externo e pode encerrar o programa.

else executa o caminho de sucesso quando o try termina normalmente, sem exceção e sem sair por uma instrução como return. Separar esse trabalho evita que o mesmo except capture por engano erros produzidos por etapas posteriores. Uma exceção levantada dentro de else ou de um tratador não é capturada pelos outros tratadores da mesma instrução try.

finally serve para ações de encerramento no fluxo normal de execução Python, inclusive quando há uma exceção a propagar. Não é uma garantia contra encerramento forçado do processo ou falha do sistema. Evite retornar de dentro desse bloco: um return ali pode substituir um retorno anterior ou suprimir uma exceção pendente.

Use raise para indicar explicitamente uma regra violada. Um valor digitado incorretamente pode permitir nova tentativa; uma falha de armazenamento pode exigir parar e avisar outra camada. O tratamento deve refletir essa diferença, em vez de produzir a mesma resposta para qualquer falha.

Executar valores aceitos e rejeitados

Salve o programa completo em exceptions_demo.py, com codificação UTF-8, numa pasta de prática. Execute python exceptions_demo.py no terminal com o ambiente virtual selecionado, se houver. Em macOS ou Ubuntu sem ambiente virtual, use python3 exceptions_demo.py quando necessário. Não há dependências externas nem arquivos de dados para criar.

def parse_amount(text):
    amount = int(text)
    if amount < 0:
        raise ValueError("amount must be nonnegative")
    return amount

for text in ["8000", "abc", "-1"]:
    try:
        amount = parse_amount(text)
    except ValueError as error:
        print("invalid:", text)
    else:
        print("accepted:", amount)
    finally:
        print("checked")

A saída esperada é:

accepted: 8000
checked
invalid: abc
checked
invalid: -1
checked

parse_amount() primeiro tenta converter o texto com int(). Para "8000", a conversão e a regra de valor não negativo passam; o retorno chega ao chamador e o bloco else imprime accepted: 8000, isto é, “aceito: 8000”. O texto "abc" falha na conversão. Já "-1" vira um inteiro, mas a função levanta seu próprio ValueError por violar a regra.

Os dois últimos casos seguem o mesmo tratador e exibem invalid, “inválido”, com a entrada. Em todos os três caminhos, finally imprime checked, “verificado”. Essa mensagem mostra que a tentativa terminou; ela não significa que o valor foi aceito. O laço continua após os erros tratados e testa o próximo texto.

Escolher o que recuperar e o que propagar

O exemplo captura ValueError porque há uma política clara: rejeitar esse texto e continuar. Se uma entrada como None for passada, int(None) gera TypeError, que esse tratador não captura. Ainda assim, finally executa antes de o erro continuar para fora. A função pressupõe texto como entrada; mudar o contrato exige decidir como tratar tipos diferentes.

Uma cláusula ampla como except Exception: pass elimina informações e pode ocultar defeitos que não eram erros recuperáveis de entrada. Quando a camada atual só consegue registrar contexto, um raise isolado dentro do except propaga a exceção original. Capturar e retornar um valor, por outro lado, faz o chamador receber esse valor e continuar normalmente; use isso apenas quando ele representa uma recuperação definida.

Quando outra camada precisa de uma exceção mais adequada ao seu contrato, raise NovoErro(...) from error permite associar a nova exceção à causa original. Isso preserva o encadeamento para diagnóstico. A documentação de erros, tratadores e encadeamento de exceções apresenta essas alternativas.

Para arquivos e outros recursos, prefira um gerenciador de contexto apropriado, como na prática de arquivos abertos com with. Uma limpeza manual em finally precisa considerar que a criação do recurso pode ter falhado: tentar fechar um objeto que nunca foi associado pode gerar outro erro e dificultar o diagnóstico do primeiro.

Comparar os casos e manter o diagnóstico útil

  1. Acrescente "0" ao fim da lista do laço. A saída adicional deve ser accepted: 0 seguida de checked. Zero atende à regra de não negatividade.
  2. Restaure a lista e acrescente uma string vazia, "". A saída adicional deve ser invalid:, com o espaço usado pelo print, seguida de checked. A falha ocorre na conversão para inteiro.
  3. Para comparar as causas num teste local, acrescente temporariamente print(type(error).__name__, str(error)) dentro do except, depois da impressão existente. O caso abc informa falha de conversão; o caso negativo mostra amount must be nonnegative, “o valor deve ser não negativo”. Ambos são ValueError, mas surgem em etapas diferentes. A redação da mensagem interna de conversão pode variar entre versões.
  4. Retire a impressão de diagnóstico e os valores adicionais. Execute novamente e confirme as seis linhas originais, com uma mensagem checked para cada entrada.

Ao lidar com dados reais, mensagens de exceção podem incluir conteúdo recebido, caminhos ou informações privadas. O exemplo só usa textos de teste; não copie indiscriminadamente mensagens e entradas para uma tela pública ou para logs. Dê ao usuário uma indicação do que ele pode corrigir e registre apenas o contexto necessário para investigar o problema.

Teste a saída de sucesso, a recuperação esperada e a propagação dos erros não tratados separadamente. Uma mensagem de finalização sozinha não comprova que o trabalho principal terminou corretamente, e a ausência de uma exceção não comprova que ela foi resolvida. A referência da instrução try e suas cláusulas permite conferir a ordem e os limites de cada caminho.