Tratamento de Exceções

Situações inesperadas sempre ocorrem durante a execução do programa — arquivos não existem, rede desconecta, usuários inserem dados inválidos... Sem lidar com essas "exceções," o programa quebra. O tratamento de exceções permite que seu programa se recupere de erros e forneça mensagens amigáveis ao usuário em vez de mensagens de erro enigmáticas.


1. O que é uma Exceção

Uma exceção é um erro que ocorre durante a execução do programa. Se uma exceção não for tratada, o programa termina:

PYTHON
# Este código vai quebrar
# print(10 / 0)               # ZeroDivisionError: division by zero
# int("abc")                  # ValueError: invalid literal for int()
# open("arquivo_inexistente.txt")    # FileNotFoundError

Use try-except para lidar com esses erros graciosamente:

Exemplo: try-except Tratando Erros de Divisão e Entrada

PYTHON
try:
    number = int(input("Digite um número: "))
    result = 10 / number
    print(f"10 / {number} = {result}")
except ValueError:
    print("Isso não é um número válido!")
except ZeroDivisionError:
    print("Não é possível dividir por zero!")
▶ Experimente

Execução de exemplo:

TEXT
Digite um número: abc
Isso não é um número válido!
💡 Filosofia de tratamento de exceções: Em vez de usar try para "prever todos os erros possíveis," trata-se de "fazer as coisas primeiro e lidar com problemas quando surgirem." A comunidade Python chama isso de: "É mais fácil pedir perdão do que permissão" (EAFP).


2. Estrutura Completa try-except

Exemplo: Estrutura Completa try-except-else-finally

PYTHON
try:
    # Código que pode dar erro
    file = open("data.txt", "r", encoding="utf-8")
    content = file.read()
    number = int(content)
    print(f"O número é: {number}")
except FileNotFoundError:
    print("Arquivo não encontrado!")
except ValueError:
    print("O conteúdo do arquivo não é um número válido!")
else:
    # Executa quando nenhuma exceção ocorre
    print("Lido e convertido com sucesso!")
finally:
    # Sempre executa, independentemente de exceções
    print("Limpeza: fechando arquivo")
    try:
        file.close()
    except NameError:
        pass
▶ Experimente
Parte Quando executa Propósito
try Sempre Coloque código propenso a erros aqui
except Quando a exceção especificada ocorre Lida com o erro
else Quando nenhuma exceção ocorre Lógica após sucesso
finally Sempre (com ou sem exceção) Limpar recursos (fechar arquivos/liberar conexões)
💡 No desenvolvimento real, else é usado com menos frequência, enquanto finally é usado extensivamente. A declaração with (context manager) gerencia a liberação de recursos automaticamente e pode frequentemente substituir finally.


3. Tipos de Exceção Comuns

PYTHON
# ZeroDivisionError — divisão por zero
# int("abc")                # ValueError — valor inválido
# [1, 2][5]                 # IndexError — índice fora do intervalo
# {"a": 1}["b"]             # KeyError — chave não existe
# open("x.txt")             # FileNotFoundError — arquivo não encontrado
# 10 + "hello"              # TypeError — erro de tipo
# import nonexistent        # ModuleNotFoundError — módulo não encontrado

Capturando Todas as Exceções (Não Recomendado)

Exemplo: Forma Correta vs Errada de Capturar Exceções

PYTHON
try:
    # Algum código
    pass
except:                     # Captura TODAS as exceções
    print("Algo deu errado")

try:
    pass
except Exception as e:      # Captura exceções conhecidas
    print(f"Erro: {e}")
▶ Experimente
⚠️ Atenção: except: sem especificação não é recomendado — ele captura exceções do sistema como KeyboardInterrupt (Ctrl+C) e SystemExit, impedindo o término normal do programa. Use except Exception as e em vez disso. A melhor prática é capturar apenas os tipos específicos de exceção que você espera.


4. raise: Levantando Exceções Explicitamente

Às vezes você precisa informar ao chamador "algo deu errado." Use raise:

Exemplo: raise para Levantar uma Exceção

PYTHON
def divide(a, b):
    if b == 0:
        raise ValueError("Não é possível dividir por zero!")
    return a / b

try:
    result = divide(10, 0)
except ValueError as e:
    print(f"Erro: {e}")     # Erro: Não é possível dividir por zero!
▶ Experimente

Exemplo de Verificação de Força de Senha

Exemplo: raise para Validação de Senha

PYTHON
def validate_password(password):
    """Valida força da senha; levanta exceção se condições não forem atendidas"""
    if len(password) < 6:
        raise ValueError("A senha deve ter pelo menos 6 caracteres")
    if not any(c.isdigit() for c in password):
        raise ValueError("A senha deve conter um dígito")
    if not any(c.isalpha() for c in password):
        raise ValueError("A senha deve conter uma letra")
    return True

try:
    validate_password("123")
except ValueError as e:
    print(f"Senha inválida: {e}")
▶ Experimente
💡 Quando usar raise? Quando uma função encontra uma situação que "não deveria acontecer" — valores de parâmetros inválidos, pré-condições não satisfeitas, dependências indisponíveis. Levante uma exceção para que o chamador decida como lidar, em vez de engolir silenciosamente o erro dentro da função.


5. Classes de Exceção Personalizadas

Você pode criar seus próprios tipos de exceção para informações de erro mais precisas:

Exemplo: Exceções Personalizadas para Saque Bancário

PYTHON
class BalanceError(Exception):
    """Exceção de saldo insuficiente"""
    pass

class AccountLockedError(Exception):
    """Exceção de conta bloqueada"""
    pass

def withdraw(balance, amount):
    if amount > balance:
        raise BalanceError(f"Saldo insuficiente! Precisa de {amount}, tem {balance}")
    if balance < 0:
        raise AccountLockedError("Conta está bloqueada")
    return balance - amount

try:
    withdraw(100, 200)
except BalanceError as e:
    print(f"Transação falhou: {e}")
except AccountLockedError as e:
    print(f"Transação falhou: {e}")
▶ Experimente
💡 Exceções personalizadas tornam seu código mais claro — chamadores podem lidar precisamente com diferentes tipos de erros. Basta herdar de Exception; geralmente apenas um nome de classe é suficiente, sem métodos extras necessários.


6. logging: Registro Profissional de Erros

Usar print() para mensagens de erro funciona em scripts simples, mas em projetos de produção, use o módulo logging:

Exemplo: Configuração e Uso de logging

PYTHON
import logging

# Configuração básica
logging.basicConfig(
    level=logging.INFO,          # Nível de log
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("app.log", encoding="utf-8"),  # Escrever em arquivo
        logging.StreamHandler()                            # Também exibir no console
    ]
)

# Diferentes níveis de log
logging.debug("Informação de depuração")        # Depuração, geralmente não exibido
logging.info("Aplicativo iniciado com sucesso")     # Informação geral
logging.warning("Uso de disco em 85%")  # Aviso
logging.error("Falha na conexão com banco de dados")   # Erro
logging.critical("Falha no sistema!")    # Erro fatal
▶ Experimente

Saída (escrita tanto em arquivo quanto no console):

TEXT
2026-06-23 16:00:00,123 [INFO] Aplicativo iniciado com sucesso
2026-06-23 16:00:00,124 [WARNING] Uso de disco em 85%
2026-06-23 16:00:00,124 [ERROR] Falha na conexão com banco de dados
💡 Benefícios de logging sobre print(): 1) Pode exibir no console e em arquivo simultaneamente. 2) Tem controle de nível (mostre tudo durante desenvolvimento, apenas WARNING+ em produção). 3) Inclui timestamps automaticamente. 4) Não precisa deletar um monte de print() antes do lançamento.


Casos de Uso Comuns


❓ Perguntas Frequentes

P: O que significa as e em except Exception as e? R: as e atribui o objeto de exceção capturado à variável e, permitindo inspecionar os detalhes do erro — print(e) mostra a descrição do erro, type(e).__name__ mostra o nome do tipo de exceção. Sem as e, você ainda pode capturar a exceção mas não pode ver informações detalhadas do erro. P: Quando devo usar exceções vs verificações if? R: Use if para erros que você pode prever antecipadamente (como verificar divisor zero). Use try-except para erros que você não pode prever (como requisições de rede, operações de arquivo). O estilo EAFP da comunidade Python tende a usar try — mas isso é uma questão de estética, e ambas as abordagens têm seus casos de uso. P: O código em finally sempre executa? R: Quase sempre — mesmo se o bloco try tiver return, break ou continue, finally ainda executa. A única exceção é quando o programa é forçadamente terminado (ex.: os._exit() ou uma queda de energia). Isso torna finally um excelente lugar para limpeza de recursos.


📖 Resumo


📝 Atividades

  1. Iniciante (Dificuldade ⭐): Escreva um programa que peça ao usuário dois números e calcule sua divisão. Use try-except para lidar com ValueError (entrada não é número) e ZeroDivisionError (divisão por zero), cada um com uma mensagem de erro amigável.

  2. Intermediário (Dificuldade ⭐⭐): Escreva uma função read_int_from_file(filename) que lê o primeiro número de um arquivo e o retorna. Use try-except para lidar com: arquivo não encontrado, conteúdo do arquivo não é um número válido, arquivo vazio. Retorne uma mensagem de erro diferente para cada caso.

  3. Avançado (Dificuldade ⭐⭐⭐): Escreva um programa "calculadora segura." O usuário digita uma expressão (ex.: 10 / 3), e o programa calcula e exibe o resultado. Requisitos: 1) Use try-except para lidar com divisão por zero, erros de valor e erros de tipo. 2) Use logging para registrar cada operação de cálculo (INFO) e erro (ERROR) em um arquivo calculator.log. 3) Suporte exit para sair. Dica: Use eval() para avaliar a expressão (note preocupações de segurança — aceitável para este exercício).

100%