Fundamentos

Exceções em Python: try, except, else, finally, raise e exceções próprias

Atualizado: · Skillaria.Top

Exceção em Python é um objeto de erro que interrompe a execução do código até ser capturado por um bloco try/except; o else roda quando não houve erro, e o finally roda sempre. Para disparar uma exceção use o comando raise, por exemplo raise ValueError("valor negativo"), e uma exceção própria é criada com uma classe herdeira de Exception.

Uma exceção em Python é um objeto que sinaliza um erro e interrompe o fluxo normal do programa. Se ninguém a capturar, o Python exibe o traceback e encerra a execução. A captura é feita com o bloco try/except, e o disparo manual — com o comando raise. Vamos ver como isso funciona, quais erros aparecem com mais frequência e como não transformar o tratamento de erros em simples ocultação deles.

O que é uma exceção em Python

Quando uma operação não pode ser executada — divisão por zero, acesso a uma chave inexistente, conversão de uma palavra em número —, o Python cria um objeto de exceção e o «lança». A execução da função atual para, a exceção sobe pela cadeia de chamadas até encontrar um tratador adequado. Se não encontrar nenhum, o programa cai.

>>> 10 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

A propósito, a resposta à pergunta frequente: na divisão por zero em Python é lançada a exceção ZeroDivisionError. Isso vale para /, // e %.

Como ler o traceback

O traceback é o relatório de como o programa chegou ao erro. É mais cômodo lê-lo de baixo para cima. A última linha é a mais importante: o tipo da exceção e a mensagem. Acima dela está a linha de código em que tudo aconteceu, e mais acima — quem a chamou, até o ponto de entrada.

Traceback (most recent call last):
  File "tracker.py", line 9, in <module>
    total = parse_amount(row)
  File "tracker.py", line 4, in parse_amount
    return float(raw.strip())
           ~~~~~^^^^^^^^^^^^^
ValueError: could not convert string to float: 'cem'

A partir do Python 3.11 sob a linha de código aparecem marcadores ~~~^^^ (PEP 657): eles mostram exatamente qual expressão da linha quebrou. Isso ajuda especialmente quando há várias chamadas seguidas numa mesma linha.

Sugestões «Did you mean»

Desde o Python 3.10, ao errar um nome, o erro sugere a variante próxima. No Python 3.15 as sugestões ficaram mais inteligentes: o interpretador conhece hábitos de outras linguagens. A chamada list.push sugere append, e "café".toUpperCase() — o método upper. A sugestão também olha campos aninhados: se um dicionário está no atributo totals do objeto e você chamou items() no próprio objeto, o erro sugerirá .totals.items. Nomes próximos agora também são sugeridos por delattr().

Tipos de exceção em Python

As exceções embutidas somam dezenas, mas no início você verá com mais frequência estas:

ExceçãoQuando apareceExemplo
NameErrorNome não definido, em geral erro de digitaçãoprint(totl)
TypeErrorOperação não se aplica a esse tipo"5" + 3
ValueErrorTipo correto, mas valor nãoint("cem")
KeyErrorNão há essa chave no dicionáriod["missing"]
IndexErrorÍndice fora da lista[1, 2][5]
AttributeErrorO objeto não tem o método ou o campo[].push(1)
ZeroDivisionErrorDivisão por zero1 / 0
FileNotFoundErrorArquivo não encontradoopen("nope.txt")

À parte está o SyntaxError (e seu caso particular IndentationError): ele surge já na leitura do arquivo, antes de o código rodar, por isso não pode ser capturado com try no mesmo arquivo.

Hierarquia de exceções

As exceções são classes, organizadas numa hierarquia. No topo está BaseException. Dele herdam Exception (quase todos os erros «comuns») e alguns de sistema: KeyboardInterrupt, SystemExit, GeneratorExit.

BaseException
 ├── KeyboardInterrupt
 ├── SystemExit
 └── Exception
      ├── ArithmeticError
      │    └── ZeroDivisionError
      ├── LookupError
      │    ├── KeyError
      │    └── IndexError
      ├── OSError
      │    └── FileNotFoundError
      ├── TypeError
      └── ValueError

A hierarquia importa na captura: except LookupError pega tanto KeyError quanto IndexError. E o KeyboardInterrupt não é um erro no código: assim o Python informa que o usuário pressionou Ctrl+C. Ele não herda de Exception de propósito, para que um except Exception não impeça a interrupção do programa.

Tratamento de exceções: try/except/else/finally

O código que pode falhar vai dentro do bloco try. No bloco except indica-se o tipo do erro e o que fazer com ele. É justamente o except que o Python usa para o tratamento de exceções.

def parse_amount(raw):
    try:
        value = float(raw)
    except ValueError as e:
        print(f"Pulando a linha: {e}")
        return None
    else:
        return value          # roda apenas se não houve erro
    finally:
        print("análise concluída")   # roda sempre

print(parse_amount("250"))

A ordem da saída aqui é: primeiro «análise concluída», depois 250.0. O return no bloco else guarda o valor, depois o Python obrigatoriamente executa o finally e só então devolve o resultado. Por isso o finally é confiável para a limpeza: fechar um arquivo, liberar uma conexão.

Se você escrever um return próprio no finally, ele substituirá o anterior e até «engolirá» a exceção. A partir do Python 3.14 esse código gera um SyntaxWarning (PEP 765) — não escreva return no finally.

Várias exceções ao mesmo tempo

É possível escrever vários blocos except para tipos diferentes ou listar os tipos em uma tupla:

try:
    amount = int(data["amount"])
except KeyError:
    amount = 0
except (ValueError, TypeError) as e:
    print("Valor estranho:", e)
    amount = 0

Os blocos são verificados de cima para baixo, e vale o primeiro que se encaixar, por isso os tipos mais específicos vêm antes dos genéricos. A partir do Python 3.14 (PEP 758) é possível listar vários tipos sem parênteses — except ValueError, TypeError: —, mas se houver as, os parênteses continuam obrigatórios.

Como exibir o texto do erro e descobrir o tipo

O objeto da exceção fica disponível por meio de as e. str(e) fornece a mensagem, e type(e).__name__ — o nome do tipo:

try:
    int("cem")
except ValueError as e:
    print(type(e).__name__)   # ValueError
    print(e)                  # invalid literal for int() with base 10: 'cem'

Módulo traceback: texto completo do erro

Às vezes o erro precisa ser capturado, mas o traceback completo deve ser preservado — para um log ou um relatório. Para isso existe o módulo traceback:

import traceback

try:
    1 / 0
except ZeroDivisionError:
    traceback.print_exc()             # imprime o traceback no stderr
    text = traceback.format_exc()     # o mesmo texto em forma de string

Se você registra logs com o módulo logging, é mais simples chamar logging.exception("não foi possível calcular") dentro do except — o traceback é anexado à mensagem automaticamente.

Python raise: como disparar uma exceção

A instrução raise lança uma exceção manualmente. Assim a função avisa que não consegue trabalhar com aqueles dados:

def add_expense(amount):
    if amount < 0:
        raise ValueError(f"O valor não pode ser negativo: {amount}")
    ...

add_expense(-5)
# ValueError: O valor não pode ser negativo: -5

O raise em Python é uma instrução, não uma função, por isso parênteses depois da palavra raise não são necessários: dentro dos parênteses passam-se os argumentos do construtor da exceção. Escolha o tipo pelo sentido: valor incorreto — ValueError, tipo incorreto — TypeError.

raise sem argumentos e raise from

Dentro de um except, um raise isolado repassa a exceção atual sem mudanças. Isso é útil quando você quer registrar algo num log, mas deixa o tratamento para quem chamou.

Se você substitui uma exceção por outra, use raise ... from e — o erro original fica preservado na cadeia, e o traceback mostra as duas:

def load_config(path):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError as e:
        raise RuntimeError(f"Sem arquivo de configuração {path}") from e

add_note: acrescentar contexto ao erro

A partir do Python 3.11 (PEP 678) é possível anexar uma observação à exceção com o método add_note() e relançá-la. O tipo do erro não muda, e a observação é impressa abaixo da mensagem:

def parse(raw, line_no):
    try:
        return float(raw)
    except ValueError as e:
        e.add_note(f"arquivo expenses.csv, linha {line_no}")
        raise

parse("cem", 17)
# ValueError: could not convert string to float: 'cem'
# arquivo expenses.csv, linha 17

Como criar uma exceção própria em Python

Exceções próprias são necessárias para erros da área do negócio: orçamento estourado, pedido já pago, usuário bloqueado. Basta uma classe herdeira de Exception:

class BudgetExceeded(Exception):
    def __init__(self, category, limit, spent):
        super().__init__(f"{category}: limite {limit}, gasto {spent}")
        self.category = category
        self.limit = limit
        self.spent = spent

def check_budget(category, limit, spent):
    if spent > limit:
        raise BudgetExceeded(category, limit, spent)

try:
    check_budget("Alimentação", 10000, 12500)
except BudgetExceeded as e:
    print("Aviso:", e)   # Aviso: Alimentação: limite 10000, gasto 12500

Agora o código chamador captura justamente o estouro de orçamento e não o confunde com erros de digitação. Num projeto grande é útil criar uma classe base comum (class TrackerError(Exception)) e herdar dela todas as exceções próprias — assim todas podem ser capturadas por um único except TrackerError. Herde de Exception, e não de BaseException.

Grupos de exceções e except*

Às vezes ocorrem vários erros de uma vez — por exemplo, ao validar um arquivo inteiro ou em tarefas paralelas com asyncio.TaskGroup. A partir do Python 3.11 eles podem ser reunidos em um ExceptionGroup, e tratados por tipo com except*:

def check_all(rows):
    errors = []
    for r in rows:
        try:
            float(r)
        except ValueError as e:
            errors.append(e)
    if errors:
        raise ExceptionGroup("erros no arquivo", errors)

try:
    check_all(["1", "a", "b"])
except* ValueError as eg:
    print(f"Linhas com problema: {len(eg.exceptions)}")   # Linhas com problema: 2

try except em Python para todos os erros: por que não fazer assim

O hábito mais prejudicial é capturar tudo: except: pass. O programa «deixa de cair», mas na prática deixa de avisar sobre problemas. O except sem tipo captura até o KeyboardInterrupt e o SystemExit, então o programa não para com Ctrl+C. O except Exception: pass é um pouco melhor, mas também engole em silêncio erros de digitação em nomes e falhas de lógica.

Como tratar exceções corretamente em Python:

  • capture apenas o tipo que você sabe tratar: except ValueError, e não tudo;
  • mantenha o bloco try curto — apenas as linhas que podem falhar daquela forma;
  • se capturar de forma ampla (except Exception) no nível superior do programa, registre obrigatoriamente o traceback;
  • não use exceções como ramificação comum onde basta uma verificação: dict.get() em vez de try/except KeyError em casos simples;
  • para recursos use with — ele fecha o arquivo mesmo em caso de erro.

Resumo

As exceções em Python são um mecanismo normal para sinalizar erros, e não um motivo para escondê-los. Leia o traceback de baixo para cima, capture tipos concretos em try/except, use else para o código «se tudo deu certo» e finally para a limpeza. O Python raise permite sinalizar um erro você mesmo, raise from e add_note preservam o contexto, e as exceções próprias herdam de Exception. No curso de Python esse tema é praticado: o controle de gastos deixa de cair por causa de uma linha de CSV malformada e expressa o estouro do orçamento com uma exceção própria.

Perguntas frequentes

Qual é a diferença entre erro e exceção em Python?

Na linguagem do dia a dia são sinônimos. Tecnicamente, o SyntaxError surge ainda na leitura do código, enquanto os demais erros acontecem durante a execução como objetos de exceção, que podem ser capturados.

Dá para continuar a execução a partir da linha onde ocorreu a exceção?

Não. Depois da captura a execução prossegue após o bloco try/except, e não a partir da linha com o erro. Se for preciso tratar cada linha de um arquivo, coloque o try dentro do laço.

O que acontece se a exceção ocorrer dentro de um except?

Ela sobe para o nível seguinte, e a exceção original fica guardada no atributo __context__. O traceback mostra a frase During handling of the above exception, another exception occurred.

Como verificar nos testes que a função lança uma exceção?

No pytest usa-se with pytest.raises(ValueError):, e no unittest — self.assertRaises(ValueError). O teste passa somente se dentro do bloco for lançada uma exceção do tipo indicado.

O que é assert e em que ele difere de raise?

assert condição, "mensagem" lança AssertionError quando a condição é falsa. É uma ferramenta de verificação durante o desenvolvimento: com a opção -O os asserts são desligados, por isso a entrada do usuário deve ser verificada com if e raise.

Aprenda este tema em vídeo

Este tema é abordado em profundidade, com prática, no curso “Python 3.15: от новичка до профи”.

Ir para o curso