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

URL: https://skillaria.top/pt/guides/python/excecoes-python-try-except

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 "", line 1, in
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
    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ção | Quando aparece | Exemplo |
| --- | --- | --- |

| `NameError` | Nome não definido, em geral erro de digitação | `print(totl)` |

| `TypeError` | Operação não se aplica a esse tipo | `"5" + 3` |

| `ValueError` | Tipo correto, mas valor não | `int("cem")` |

| `KeyError` | Não há essa chave no dicionário | `d["missing"]` |

| `IndexError` | Índice fora da lista | `[1, 2][5]` |

| `AttributeError` | O objeto não tem o método ou o campo | `[].push(1)` |

| `ZeroDivisionError` | Divisão por zero | `1 / 0` |

| `FileNotFoundError` | Arquivo não encontrado | `open("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.

## FAQ

**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.


---
Source: Skillaria.Top (https://skillaria.top)
