Trilha de aprendizado · Nível 6 · Tutorial 1

Tratar exceções com try, except, else e finally

Interpretar um traceback com várias chamadas e tratar falhas esperadas sem esconder erros inesperados, distinguindo os caminhos de execução dos blocos de tratamento.

  • Nível: Intermediário
  • Duração: 25 min
  • 8 passos
Tratar exceções com try, except, else e finally

O que você vai percorrer

  1. Reconstruir a sequência de chamadas pelo traceback Leia um traceback com várias funções, reconstrua a ordem das chamadas e diferencie os chamadores da operação que realmente falhou. 3 min
  2. Tratar uma exceção específica no ponto certo Use try e except para tratar um ValueError, acompanhar sua propagação entre funções e entender onde a execução continua. 4 min
  3. Escolher entre tratamentos separados e agrupados Organize capturas alternativas de acordo com a resposta necessária para cada tipo de falha e preserve a propagação de erros não previstos. 3 min
  4. Separar o caminho de sucesso com else Use else para executar somente as ações que dependem da conclusão bem-sucedida do bloco try e reconheça que falhas surgidas no próprio else continuam se propagando. 3 min
  5. Executar ações de encerramento com finally Preveja quando o bloco finally é executado e diferencie encerramento da estrutura de tratamento ou recuperação de uma exceção. 3 min
  6. Evitar que finally altere o resultado da função Reconheça como um return em finally pode substituir outro retorno ou ocultar uma exceção, e organize a função para preservar seu resultado real. 3 min
  7. Restringir o tratamento às falhas esperadas Reduza o escopo do try para tratar apenas falhas previstas pelo contexto e manter erros inesperados visíveis. 3 min
  8. Aplicar e revisar os quatro blocos Refatore um programa em memória, execute quatro cenários separadamente e interprete a falha que deve continuar visível. 4 min

O que você vai aprender

  • Relacionar os quadros de um traceback às chamadas que levaram à falha.
  • Capturar tipos específicos de exceção e acessar a mensagem do erro.
  • Prever quais blocos serão executados quando uma operação tiver sucesso, produzir uma exceção tratada ou produzir uma exceção não tratada.
  • Delimitar o bloco try para evitar a captura acidental de falhas de outras operações.

Antes de começar

  • Localizar e corrigir erros básicos
  • Definir funções com parâmetros e retorno

Passo 1 de 8

Reconstruir a sequência de chamadas pelo traceback

Leia um traceback com várias funções, reconstrua a ordem das chamadas e diferencie os chamadores da operação que realmente falhou.

Um programa, várias chamadas

Acompanhe as funções encadeadas

O script chama executar, que chama calcular_metade, que chama converter. O erro surge na função mais interna, mas o traceback também registra as chamadas que levaram até ela. Considere as linhas em branco ao acompanhar a numeração.

exemplo.py

python
def converter(texto):
    return int(texto)


def calcular_metade(texto):
    numero = converter(texto)
    return numero / 2


def executar():
    return calcular_metade("dez")


executar()

Traceback produzido

O caminho exibido para o arquivo pode variar no seu computador.

text
Traceback (most recent call last):
  File "exemplo.py", line 14, in <module>
    executar()
  File "exemplo.py", line 11, in executar
    return calcular_metade("dez")
  File "exemplo.py", line 6, in calcular_metade
    numero = converter(texto)
  File "exemplo.py", line 2, in converter
    return int(texto)
ValueError: invalid literal for int() with base 10: 'dez'

Leia do resultado para o percurso

Tipo, mensagem e quadros

Comece pela última linha: ValueError é o tipo da exceção, e o texto após os dois-pontos é sua mensagem. Depois, examine os quadros acima. Cada quadro informa o arquivo, o número da linha e o local de execução — uma função ou <module>, quando a linha está no nível principal do script.

Os quadros aparecem da chamada mais externa para o ponto mais interno apresentado: linha 14 → linha 11 → linha 6 → linha 2.

Do chamador externo à origem

Diagrama com quatro quadros conectados por setas, numerados 14, 11, 6 e 2; o último está destacado como ponto da falha.

As linhas 14, 11 e 6 fazem chamadas. Na linha 2, a conversão int(texto) é a operação que falha.

Dica

Chamador não é origem

Uma linha presente no traceback pode apenas ter chamado outra função. Neste exemplo, numero = converter(texto) conduz à função interna; quem realmente produz a exceção é int(texto), ao tentar converter "dez".

Reconstrua o traceback

Associe cada quadro ao que aconteceu

Relacione os quadros do traceback às ações executadas.

Toque em um item e depois no par correspondente.

Localize a origem

Qual operação originou a exceção?

Passo 2 de 8

Tratar uma exceção específica no ponto certo

Use try e except para tratar um ValueError, acompanhar sua propagação entre funções e entender onde a execução continua.

A falha desvia o fluxo imediatamente

Coloque no try a operação que pode produzir a falha esperada. Se int() não conseguir converter o texto, ele produz um ValueError: a execução abandona imediatamente o restante do try e procura um except compatível.

Conversão protegida

Execute mentalmente o código e observe que as mensagens 3 e 4 não são exibidas.

python
print("1. antes do try")

try:
    print("2. iniciando a conversão")
    quantidade = int("doze")
    print("3. conversão concluída")
    print(f"4. quantidade: {quantidade}")
except ValueError as erro:
    print(f"5. entrada inválida: {erro}")

print("6. depois da estrutura")

Caminho percorrido após a falha

Diagrama em duas faixas mostrando o fluxo saindo do bloco try no ponto da conversão inválida, seguindo para except ValueError e continuando após a estrutura.

A falha interrompe o try, desvia para o except compatível e, quando o tratamento termina normalmente, a execução continua depois da estrutura.

A exceção pode chegar ao chamador

Tratamento fora da função que falhou

A conversão ocorre em converter, mas o tratamento está em mostrar_quantidade, que chamou essa função.

python
def converter(texto):
    print("conversão iniciada")
    numero = int(texto)
    print("conversão terminada")
    return numero


def mostrar_quantidade(texto):
    try:
        quantidade = converter(texto)
        print(f"quantidade: {quantidade}")
    except ValueError as erro:
        print(f"não foi possível converter: {erro}")

    print("fim de mostrar_quantidade")


mostrar_quantidade("três")

Propagação e continuação

O ValueError sai de converter e se propaga até o except ValueError no chamador. O tratamento não retoma a função no ponto interrompido: conversão terminada não é exibida, nem um valor é retornado por aquela chamada.

Em except ValueError as erro, a escolha do tratamento depende do tipo ValueError. O nome erro dá acesso ao objeto da exceção e à sua mensagem, que pode ser exibida em uma f-string. Se a exceção não for compatível com ValueError, ela continuará se propagando; nesse caso, o código após a estrutura não será alcançado por esse fluxo.

Complete a captura específica

Complete o tipo da exceção para tratar a conversão e acessar sua mensagem:

try:
    idade = int(texto)
except ____ as erro:
    print(f"idade inválida: {erro}")

Ordene o fluxo da execução

Considere o código abaixo e ordene somente as mensagens que serão exibidas:

def converter(texto):
    print("B")
    return int(texto)

print("A")
try:
    valor = converter("x")
    print("C")
except ValueError as erro:
    print("D")

print("E")
  1. A
  2. E
  3. D
  4. B

Passo 3 de 8

Escolher entre tratamentos separados e agrupados

Organize capturas alternativas de acordo com a resposta necessária para cada tipo de falha e preserve a propagação de erros não previstos.

Respostas diferentes, cláusulas separadas

Um tratamento para cada resposta

Quando cada tipo de falha exige uma resposta diferente, use cláusulas except separadas. Neste exemplo, uma conversão inválida produz ValueError, enquanto a divisão por zero produz ZeroDivisionError.

Capturas específicas

Observe que cada cláusula produz uma mensagem adequada ao tipo de falha.

python
def calcular_razao(texto_total, texto_partes):
    try:
        total = int(texto_total)
        partes = int(texto_partes)
        return total / partes
    except ValueError as erro:
        return f"Conversão inválida: {erro}"
    except ZeroDivisionError as erro:
        return f"Divisão por zero: {erro}"


print(calcular_razao("dez", "2"))
print(calcular_razao("10", "0"))
print(calcular_razao("10", "2"))

Somente a primeira cláusula compatível

A procura acontece em ordem

Quando uma exceção sai do try, o Python examina as cláusulas except de cima para baixo. Ele executa somente a primeira compatível e não continua procurando outros tratamentos. Se nenhuma for compatível, a exceção continua se propagando.

Três destinos possíveis

O fluxo pode chegar ao tratamento de conversão, ao tratamento de divisão por zero ou sair da estrutura sem tratamento.

Fluxograma em que ValueError segue para o primeiro tratamento, ZeroDivisionError segue para o segundo e TypeError continua se propagando.

As duas falhas previstas são tratadas; TypeError não corresponde a nenhuma das capturas declaradas.

Dica

A ordem não significa executar todas

Mesmo com várias cláusulas compatíveis possíveis, apenas a primeira encontrada seria executada. Neste exemplo, ValueError e ZeroDivisionError são distintos, então cada um chega diretamente ao seu tratamento específico.

Uma resposta igual permite agrupar

Agrupe pela resposta desejada

Se as falhas devem receber exatamente o mesmo tratamento, coloque os tipos em uma tupla. O agrupamento deve representar uma decisão do programa, não apenas uma tentativa de reduzir linhas.

Captura agrupada

Agora as duas falhas previstas recebem a mesma resposta.

python
def calcular_razao(texto_total, texto_partes):
    try:
        total = int(texto_total)
        partes = int(texto_partes)
        return total / partes
    except (ValueError, ZeroDivisionError) as erro:
        return f"Não foi possível calcular: {erro}"


print(calcular_razao("dez", "2"))
print(calcular_razao("10", "0"))

Atenção

Agrupar não captura outros tipos

A chamada calcular_razao(None, "2") produz TypeError em int(None). Como esse tipo não aparece na tupla, a falha permanece visível e continua se propagando.

Decida o destino de cada falha

Relacione chamada e resultado

Considere a versão com cláusulas separadas. Associe cada chamada ao seu destino.

Toque em um item e depois no par correspondente.

Separar ou agrupar?

O programa deve pedir a correção do texto quando ocorrer ValueError, mas informar que o divisor não pode ser zero quando ocorrer ZeroDivisionError. Qual organização atende melhor ao requisito?

Passo 4 de 8

Separar o caminho de sucesso com else

Use else para executar somente as ações que dependem da conclusão bem-sucedida do bloco try e reconheça que falhas surgidas no próprio else continuam se propagando.

Um bloco exclusivo para o sucesso

Quando o else é executado?

O bloco else associado a try é executado somente quando o try chega ao fim sem produzir uma exceção. Se uma exceção surgir no try, o fluxo procura um except compatível e não passa pelo else — mesmo quando a exceção é tratada.

Isso permite deixar no try apenas a operação sujeita à falha esperada e mover para o else as ações que dependem do resultado bem-sucedido.

Conversão no try, cálculo no else

Execute o código e compare as duas chamadas.

python
def mostrar_metade(texto):
    try:
        numero = int(texto)
    except ValueError as erro:
        print(f"Não foi possível converter: {erro}")
    else:
        metade = numero / 2
        print(f"Metade: {metade}")


mostrar_metade("8")
mostrar_metade("oito")

Três caminhos possíveis

O destino depende de onde a falha ocorre

Há três caminhos importantes:

  • Se o try terminar sem exceção, o fluxo segue para o else.
  • Se o try produzir uma exceção compatível, o fluxo segue para o except e ignora o else.
  • Se o try terminar com sucesso, mas uma operação no else falhar, essa nova exceção sai da estrutura e continua se propagando.

Fluxos de try, except e else

Diagrama com três fluxos: sucesso do try seguindo para else, falha no try seguindo para except e falha no else propagando para fora da estrutura.

O except associado à estrutura examina falhas do try, não as falhas produzidas depois, dentro do else.

Uma falha produzida dentro do else

O except não protege o próprio else

No código abaixo, int("0") termina normalmente, então o else é iniciado. A divisão por zero acontece dentro dele. Como os except desta estrutura tratam exceções produzidas no try, o ZeroDivisionError não é capturado pelo except ValueError e continua se propagando.

Sucesso na conversão, falha no processamento

Ao executar, a primeira mensagem aparece antes do traceback.

python
def calcular_inverso(texto):
    try:
        numero = int(texto)
    except ValueError as erro:
        print(f"Entrada inválida: {erro}")
    else:
        print("Conversão concluída")
        inverso = 1 / numero
        print(f"Inverso: {inverso}")


calcular_inverso("0")

Dica

Por que separar as operações?

Manter apenas a conversão no try deixa claro que o tratamento foi planejado para uma falha dessa conversão. O processamento que depende do número convertido fica no else, e suas próprias falhas não são confundidas com a falha esperada.

Preveja o fluxo

Analise sem executar primeiro

python
def procurar(texto):
    try:
        numero = int(texto)
    except ValueError:
        print("Entrada inválida")
    else:
        print("Conversão concluída")
        posicao = ["10", "20"].index(texto)
        print(numero, posicao)


procurar("30")

Qual será o resultado?

O que acontece na chamada procurar("30")?

Passo 5 de 8

Executar ações de encerramento com finally

Preveja quando o bloco finally é executado e diferencie encerramento da estrutura de tratamento ou recuperação de uma exceção.

Todos os caminhos passam por finally

Encerramento da estrutura

O bloco finally é executado quando o fluxo sai da estrutura try, seja após o sucesso, após um except ou enquanto uma exceção não tratada está se propagando.

Nos caminhos mais comuns:

  • sucesso: try → else → finally → depois da estrutura;
  • exceção tratada: try → except → finally → depois da estrutura;
  • exceção não tratada: try → finally → propagação.

Executar finally não significa que o erro foi tratado. Se ainda houver uma exceção pendente e o finally terminar normalmente, ela continuará se propagando.

Convergência dos caminhos

Observe que todos os caminhos chegam ao bloco dourado. Somente os caminhos sem uma exceção pendente seguem para a continuação normal.

Diagrama com caminhos de sucesso, falha tratada e falha não tratada convergindo em um bloco dourado que representa finally.

Azul representa try, verde representa else, laranja representa except e dourado representa finally. A seta vermelha indica a propagação de uma exceção pendente.

Comparar três execuções

Acompanhe os eventos em memória

A função abaixo registra a passagem pelos blocos em uma lista. Execute cada chamada separadamente, pois o terceiro cenário termina com uma exceção não tratada.

Sucesso, falha tratada e falha no else

python
def executar(texto, divisor):
    eventos = []

    try:
        eventos.append("try")
        numero = int(texto)
    except ValueError as erro:
        eventos.append(f"except: {erro}")
    else:
        eventos.append("else")
        resultado = 10 / divisor
        eventos.append(f"resultado: {resultado}")
    finally:
        eventos.append("finally")
        print(eventos)

    print("depois da estrutura")


# Execute uma chamada por vez:
executar("5", 2)    # sucesso
# executar("abc", 2)  # ValueError tratada
# executar("5", 0)    # ZeroDivisionError no else

Dica

O que observar

Com "5" e 2, passam try, else e finally, e a execução continua. Com "abc", passam try, except e finally, e também há continuação. Com divisor zero, a falha surge no else: finally ainda imprime a lista, mas depois da estrutura não aparece porque ZeroDivisionError continua se propagando.

Falha durante o próprio tratamento

Finally também acompanha falhas posteriores

Uma nova exceção pode surgir durante um except, assim como pode surgir no else. Nesses casos, finally ainda é executado antes da propagação. Ele realiza sua ação de encerramento, mas não recupera o programa automaticamente.

Exceção produzida dentro de except

python
def falhar_durante_tratamento():
    eventos = []

    try:
        int("inválido")
    except ValueError:
        eventos.append("except")
        10 / 0
    finally:
        eventos.append("finally")
        print(eventos)

    print("depois da estrutura")


falhar_durante_tratamento()

Atenção

Encerrar não é capturar

Nesse exemplo, finally imprime ['except', 'finally']. Em seguida, ZeroDivisionError permanece visível no traceback, e a mensagem depois da estrutura não é executada.

Preveja o fluxo

Ordene o cenário de falha no else

Coloque em ordem os acontecimentos de executar("5", 0).

  1. O else começa.
  2. ZeroDivisionError continua se propagando, sem executar a mensagem posterior.
  3. O try começa e a conversão de "5" termina normalmente.
  4. A divisão produz ZeroDivisionError no else.
  5. O finally é executado e imprime os eventos.

E quando a falha é tratada?

Qual sequência descreve executar("abc", 2)?

Passo 6 de 8

Evitar que finally altere o resultado da função

Reconheça como um return em finally pode substituir outro retorno ou ocultar uma exceção, e organize a função para preservar seu resultado real.

O retorno ainda precisa passar pelo finally

Um retorno pendente

Quando o Python encontra return dentro de try, ele prepara o valor de retorno, mas executa finally antes de sair da função. Como o try não chegou normalmente ao fim, o bloco else não é executado.

Se o próprio finally executar outro return, esse novo retorno substituirá o anterior.

O finally substitui o primeiro retorno

Execute o código e observe que a mensagem do else não aparece.

python
def obter_pontuacao():
    try:
        return 10
    else:
        print("Try concluído sem retorno antecipado")
    finally:
        print("Encerrando cálculo")
        return 99


print(obter_pontuacao())

# Saída:
# Encerrando cálculo
# 99

Atenção

Evite decisões de retorno em finally

O valor 10 estava pronto para ser devolvido, mas o return 99 do finally tomou seu lugar. Isso torna o resultado da função diferente daquele indicado pelo fluxo principal.

Uma exceção também pode ser ocultada

Return não é tratamento de exceção

Se houver uma exceção pendente, o finally ainda será executado. Porém, um return dentro dele encerra a função com um valor e suprime a exceção. A ação de encerramento aconteceu, mas a falha inesperadamente deixou de se propagar.

O que atravessa o finally

Compare os caminhos: um encerramento normal permite que o retorno ou a exceção pendente continue; um novo retorno criado no finally ocupa a saída e descarta o que estava pendente.

Diagrama comparando um retorno e uma exceção que atravessam uma etapa de encerramento com caminhos em que um novo retorno substitui ambos.

O finally deve executar o encerramento sem trocar o retorno nem apagar a exceção pendente.

A conversão falha, mas parece retornar um valor

int("abc") produz ValueError, mas o return de finally oculta essa falha.

python
def converter(texto):
    try:
        return int(texto)
    finally:
        print("Encerrando conversão")
        return 0


print(converter("abc"))

# Saída:
# Encerrando conversão
# 0

Preserve o resultado real

Deixe o finally apenas encerrar

Mantenha no finally somente as ações que devem ocorrer em qualquer caminho. O tratamento específico, o processamento de sucesso e a decisão de retorno ficam fora dele.

Versão corrigida

Agora o encerramento sempre ocorre, mas não altera a decisão tomada pelos outros blocos.

python
def dobrar_texto_numerico(texto):
    try:
        numero = int(texto)
    except ValueError as erro:
        resultado = f"Entrada inválida: {erro}"
    else:
        resultado = numero * 2
    finally:
        print("Encerrando conversão")

    return resultado


print(dobrar_texto_numerico("21"))
print(dobrar_texto_numerico("abc"))

Dica

Regra prática

Use finally para encerramento, não para escolher o resultado da função. Sem return nesse bloco, um retorno pendente permanece válido e uma exceção pendente continua visível.

Explique e corrija

Dois efeitos de return em finally

Analise as duas funções antes de responder.

python
def primeira():
    try:
        return 8
    finally:
        return 12


def segunda():
    try:
        return int("x")
    finally:
        return -1


print(primeira())
print(segunda())

Preveja os resultados

O que cada chamada imprime? Por que a segunda função não exibe um traceback? Indique uma correção que mantenha uma ação de encerramento em finally sem alterar retornos nem ocultar exceções.

Escreva pelo menos 100 caracteres (0/100).

Passo 7 de 8

Restringir o tratamento às falhas esperadas

Reduza o escopo do try para tratar apenas falhas previstas pelo contexto e manter erros inesperados visíveis.

O contexto define a falha esperada

Mesmo tipo, significados diferentes

Uma exceção é esperada quando faz parte das falhas previstas para uma operação específica. Por exemplo, ao converter um texto fornecido pelo usuário com int, um ValueError pode ser uma entrada que o programa sabe tratar. Mas um ValueError em outra operação pode indicar um defeito.

Por isso, não basta perguntar “qual é o tipo da exceção?”. Também é necessário perguntar “qual operação poderia produzir esse erro e por que ele é esperado aqui?”.

Uma fronteira estreita para o tratamento

O try deve envolver somente a operação cuja falha foi prevista. Operações posteriores ficam fora dessa fronteira para que seus erros não sejam confundidos com a falha esperada.

Comparação visual entre um bloco try amplo, que captura uma conversão esperada e um defeito posterior, e um bloco try estreito, que envolve apenas a conversão.

À esquerda, a captura ampla mistura falhas diferentes. À direita, somente a conversão esperada está dentro do try.

O perigo de um try extenso

Captura excessivamente abrangente

A conversão pode falhar com ValueError, mas list.index também produz esse tipo quando não encontra o item. O código abaixo transforma até o erro de digitação interno em uma mensagem sobre a entrada.

python
def preparar_pedido(texto_quantidade):
    etapas = ["recebido", "pago", "enviado"]

    try:
        quantidade = int(texto_quantidade)
        posicao = etapas.index("enviadoo")
    except ValueError as erro:
        return f"Entrada inválida: {erro}"
    else:
        return quantidade, posicao


print(preparar_pedido("12"))

Try restrito e processamento no else

Agora, somente a conversão está no try. Uma entrada como "doze" recebe o tratamento planejado. Com "12", o erro de digitação em index permanece visível porque exceções produzidas no else não são capturadas pelo except associado.

python
def preparar_pedido(texto_quantidade):
    etapas = ["recebido", "pago", "enviado"]

    try:
        quantidade = int(texto_quantidade)
    except ValueError as erro:
        return f"Entrada inválida: {erro}"
    else:
        posicao = etapas.index("enviadoo")
        return quantidade, posicao


print(preparar_pedido("12"))

Dica

Falhar visivelmente pode ser o comportamento correto

Deixar uma falha inesperada se propagar preserva o traceback e evita devolver uma mensagem ou um valor que pareça indicar funcionamento normal. Corrigir o defeito é melhor do que classificá-lo incorretamente como uma entrada inválida.

Capturas amplas escondem defeitos

Capture somente o que você pretende recuperar

except Exception captura muitos erros que podem não fazer parte da recuperação planejada, como falhas de índice, operações com tipos incompatíveis ou divisões por zero. Se todos virarem None, 0 ou uma mensagem genérica, defeitos podem passar despercebidos.

Um except sem tipo tem alcance ainda maior e pode interceptar inclusive uma interrupção feita pelo usuário pelo teclado. Para uma falha conhecida, prefira o tipo específico e um try restrito à operação correspondente.

Atenção

Não transforme erro desconhecido em sucesso aparente

Evite padrões como except Exception: return 0 ou except: pass. Se 0 ou a continuação silenciosa parecerem resultados válidos, o restante do programa poderá trabalhar com uma informação incorreta sem revelar a causa original.

Delimite o tratamento

Código para analisar

A conversão de texto_indice é a única falha esperada neste cenário. As operações com a lista devem manter visíveis falhas não previstas.

python
def calcular_parcela(texto_indice, precos):
    try:
        indice = int(texto_indice)
        preco = precos[indice]
        parcela = preco / indice
    except Exception:
        return 0
    else:
        return parcela

Refatore e justifique

Refatore a função para tratar especificamente a falha de conversão. Coloque no else as operações que dependem do índice convertido e explique por que as demais exceções não devem ser transformadas em 0.

Escreva pelo menos 80 caracteres (0/80).

Passo 8 de 8

Aplicar e revisar os quatro blocos

Refatore um programa em memória, execute quatro cenários separadamente e interprete a falha que deve continuar visível.

Refatore o tratamento

O script inicial esconde falhas demais

Copie o script abaixo para um arquivo .py no seu computador. Ele calcula uma razão usando apenas dados em memória, mas apresenta três problemas: o try inclui processamento posterior, except Exception captura também a falha inesperada e há um return dentro de finally.

Refatore a função para:

  1. manter no try somente as conversões e a divisão;
  2. tratar ValueError e ZeroDivisionError separadamente;
  3. acessar cada mensagem com as erro;
  4. colocar o cálculo com fator e a mensagem de sucesso no else;
  5. registrar o encerramento no finally, sem executar return nele.

Script inicial completo

Altere somente a função calcular_razao. Para cada execução, escolha um dos quatro nomes em CENARIO.

python
def calcular_razao(texto_numerador, texto_denominador, fator):
    eventos = []

    try:
        numerador = int(texto_numerador)
        denominador = int(texto_denominador)
        resultado = numerador / denominador
        ajustado = resultado * fator
    except Exception as erro:
        eventos.append(f"falha: {erro}")
    else:
        eventos.append(f"resultado: {ajustado:.2f}")
    finally:
        eventos.append("encerramento")
        print(" | ".join(eventos))
        return eventos


CENARIO = "sucesso"

casos = {
    "sucesso": ("12", "3", 2),
    "conversao": ("doze", "3", 2),
    "divisao_zero": ("12", "0", 2),
    "inesperada": ("12", "3", "dobro"),
}

texto_a, texto_b, fator = casos[CENARIO]
calcular_razao(texto_a, texto_b, fator)

Atenção

Execute um cenário por vez

Troque o valor de CENARIO e reinicie o script em cada teste. A falha inesperada deve encerrar sua própria execução com um traceback; por isso, não dependa de uma única execução para observar todos os casos.

Compare com a refatoração

Uma solução possível

Nesta versão, somente as falhas esperadas das conversões e da divisão são capturadas. O processamento que depende do resultado fica no else. Se esse processamento falhar, os except associados ao mesmo try não interceptam a nova exceção.

Função refatorada

Substitua a função inicial por esta versão e mantenha o restante do script.

python
def calcular_razao(texto_numerador, texto_denominador, fator):
    eventos = []

    try:
        numerador = int(texto_numerador)
        denominador = int(texto_denominador)
        resultado = numerador / denominador
    except ValueError as erro:
        eventos.append(f"conversão inválida: {erro}")
    except ZeroDivisionError as erro:
        eventos.append(f"divisão inválida: {erro}")
    else:
        ajustado = resultado * fator
        eventos.append(f"resultado: {ajustado:.2f}")
    finally:
        eventos.append("encerramento")
        print(" | ".join(eventos))

Os quatro caminhos da função

Observe que todos os caminhos passam pelo encerramento, mas somente três terminam normalmente.

Diagrama com quatro caminhos: sucesso passa por try, else e finally; conversão inválida e divisão por zero passam por try, except e finally; falha inesperada passa por try, else e finally antes de continuar como erro.

O finally registra o encerramento, mas não transforma a falha inesperada em sucesso.

Execute e interprete

Resultados que você deve investigar

Execute separadamente sucesso, conversao, divisao_zero e inesperada.

No sucesso, procure resultado: 8.00 | encerramento. Nos dois erros esperados, confirme a mensagem específica seguida de encerramento. No cenário inesperado, o finally imprime encerramento, mas depois aparece um traceback de TypeError.

Leia esse traceback de fora para dentro: localize primeiro a chamada de calcular_razao no código de nível superior e depois a multiplicação resultado * fator dentro da função. A última linha informa o tipo e a mensagem da falha.

Registre sua verificação

Quais blocos foram executados em cada cenário? Por que ValueError e ZeroDivisionError foram tratados, enquanto TypeError permaneceu visível? Relacione também os quadros do traceback às chamadas do programa.

Escreva pelo menos 180 caracteres (0/180).

Critérios para um tratamento seguro

Resumo

Revisão final

Use estes critérios ao decidir como tratar uma falha esperada.

  • Leia a última linha do traceback para identificar o tipo e a mensagem; use os quadros para reconstruir as chamadas até a operação que falhou.
  • Mantenha no try apenas as operações cujas falhas você planejou tratar.
  • Capture tipos específicos e use as erro quando precisar apresentar ou registrar a mensagem.
  • Coloque no else as ações que dependem do sucesso completo do try.
  • Use finally para ações de encerramento, não para ocultar exceções nem substituir resultados com return.
  • Permita que falhas inesperadas continuem se propagando, em vez de convertê-las em resultados aparentemente normais.

Tutorial concluído

Parabéns! Você concluiu: Tratar exceções com try, except, else e finally

Você concluiu o tutorial e já pode tratar falhas esperadas sem esconder erros inesperados.

100 XP

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso