Tratamento de Erros em Python — 2025 | Python Brasil
Domine o tratamento de erros em Python com try/except, exceções customizadas e boas práticas. Aprenda agora!
Erros fazem parte do desenvolvimento de software. A diferença entre um programa amador e um profissional está em como esses erros são tratados. Python possui um sistema robusto de exceções que permite capturar, tratar e até criar seus próprios tipos de erro, garantindo que seu programa se comporte de maneira previsível mesmo em situações inesperadas.
Neste artigo, vamos explorar desde o básico do try/except até técnicas avançadas como exceções personalizadas e padrões de tratamento de erros usados em projetos profissionais.
Entendendo exceções em Python
Uma exceção é um evento que interrompe o fluxo normal de execução do programa. Quando algo dá errado, Python levanta (raises) uma exceção. Se ela não for tratada, o programa encerra com uma mensagem de erro.
# Exemplos de exceções comuns
# print(10 / 0) # ZeroDivisionError
# print(int("abc")) # ValueError
# lista = [1, 2, 3]
# print(lista[10]) # IndexError
# dicionario = {"a": 1}
# print(dicionario["b"]) # KeyError
# import modulo_inexistente # ModuleNotFoundError
Cada tipo de exceção indica uma categoria específica de problema. Conhecer os tipos mais comuns ajuda a escrever tratamentos mais precisos.
Try, except, else e finally
A estrutura completa de tratamento de exceções em Python tem quatro blocos:
def dividir(a, b):
"""Demonstra a estrutura completa de tratamento de exceções."""
try:
resultado = a / b
except ZeroDivisionError:
print("Erro: divisão por zero não é permitida.")
return None
except TypeError as e:
print(f"Erro de tipo: {e}")
return None
else:
# Executado apenas se NÃO houve exceção
print(f"Divisão realizada com sucesso: {a} / {b}")
return resultado
finally:
# Executado SEMPRE, com ou sem exceção
print("Operação de divisão finalizada.")
# Testando
print(dividir(10, 3)) # Sucesso
print(dividir(10, 0)) # ZeroDivisionError
print(dividir("10", 3)) # TypeError
O bloco else é frequentemente subutilizado, mas é valioso: ele deixa claro que determinado código só deve executar quando não houve erro. O bloco finally é ideal para liberar recursos como conexões de banco de dados e arquivos abertos.
Capturando múltiplas exceções
Existem diferentes formas de capturar mais de um tipo de exceção:
def processar_dado(valor):
"""Processa um dado com múltiplos tratamentos."""
try:
numero = int(valor)
resultado = 100 / numero
return resultado
except (ValueError, TypeError) as e:
# Captura múltiplas exceções no mesmo bloco
print(f"Erro na conversão: {e}")
return None
except ZeroDivisionError:
print("O valor não pode ser zero.")
return None
# Testando
print(processar_dado("5")) # 20.0
print(processar_dado("abc")) # Erro na conversão
print(processar_dado("0")) # Não pode ser zero
print(processar_dado(None)) # Erro na conversão
Evite capturar Exception genericamente sem necessidade. Tratar exceções específicas torna o código mais previsível e facilita a identificação de problemas.
Levantando exceções
Às vezes, é seu código que precisa sinalizar um erro. Use raise para levantar exceções:
def validar_idade(idade):
"""Valida se a idade está dentro dos limites aceitáveis."""
if not isinstance(idade, int):
raise TypeError(f"Idade deve ser um inteiro, recebeu {type(idade).__name__}")
if idade < 0:
raise ValueError("Idade não pode ser negativa.")
if idade > 150:
raise ValueError("Idade não pode ser maior que 150.")
return True
def cadastrar_usuario(nome, idade):
"""Cadastra um usuário com validação."""
try:
validar_idade(idade)
print(f"Usuário '{nome}' cadastrado com idade {idade}.")
except (TypeError, ValueError) as e:
print(f"Erro no cadastro: {e}")
cadastrar_usuario("Ana", 25) # Sucesso
cadastrar_usuario("Bruno", -5) # Erro: negativa
cadastrar_usuario("Carla", "vinte") # Erro: tipo
Exceções personalizadas
Para projetos maiores, criar suas próprias exceções melhora significativamente a clareza do código:
class ErroAplicacao(Exception):
"""Exceção base para erros da aplicação."""
pass
class ErroValidacao(ErroAplicacao):
"""Erro de validação de dados."""
def __init__(self, campo, mensagem):
self.campo = campo
self.mensagem = mensagem
super().__init__(f"Validação falhou no campo '{campo}': {mensagem}")
class ErroAutenticacao(ErroAplicacao):
"""Erro de autenticação do usuário."""
def __init__(self, mensagem="Credenciais inválidas"):
self.mensagem = mensagem
super().__init__(mensagem)
class ErroPermissao(ErroAplicacao):
"""Erro de permissão insuficiente."""
def __init__(self, recurso, acao):
self.recurso = recurso
self.acao = acao
super().__init__(f"Sem permissão para {acao} o recurso '{recurso}'")
# Usando exceções personalizadas
def validar_email(email):
if not email or "@" not in email:
raise ErroValidacao("email", "Formato de e-mail inválido")
return True
def autenticar(usuario, senha):
usuarios_validos = {"admin": "senha123"}
if usuario not in usuarios_validos or usuarios_validos[usuario] != senha:
raise ErroAutenticacao()
return True
try:
validar_email("usuario_sem_arroba")
except ErroValidacao as e:
print(f"Campo: {e.campo}")
print(f"Mensagem: {e.mensagem}")
try:
autenticar("admin", "senha_errada")
except ErroAutenticacao as e:
print(f"Falha na autenticação: {e.mensagem}")
Criar uma hierarquia de exceções permite capturar erros em diferentes níveis de granularidade. Capturar ErroAplicacao pega todos os erros da sua aplicação, enquanto ErroValidacao captura apenas erros de validação.
Padrão de contexto seguro
O gerenciador de contexto (with) é a maneira mais segura de lidar com recursos que precisam ser liberados:
class ConexaoBanco:
"""Simula uma conexão com banco de dados."""
def __init__(self, nome_banco):
self.nome_banco = nome_banco
self.conectado = False
def __enter__(self):
self.conectado = True
print(f"Conectado ao banco '{self.nome_banco}'")
return self
def __exit__(self, tipo_exc, valor_exc, traceback):
self.conectado = False
print(f"Desconectado do banco '{self.nome_banco}'")
if tipo_exc is not None:
print(f"Erro durante operação: {valor_exc}")
return False # Não suprime a exceção
def executar(self, query):
if not self.conectado:
raise RuntimeError("Não conectado ao banco.")
print(f"Executando: {query}")
# Uso seguro com with
with ConexaoBanco("meu_banco") as db:
db.executar("SELECT * FROM usuarios")
db.executar("UPDATE config SET valor = 'novo'")
# A conexão é fechada automaticamente, mesmo se houver erro
Logging de exceções
Em produção, imprimir erros no console não é suficiente. Use o módulo logging para registrar exceções de forma adequada:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
def processar_pedido(pedido_id):
"""Processa um pedido com logging adequado."""
logger.info(f"Iniciando processamento do pedido {pedido_id}")
try:
# Simulação de processamento
if pedido_id <= 0:
raise ValueError("ID do pedido deve ser positivo")
logger.info(f"Pedido {pedido_id} processado com sucesso")
return True
except ValueError as e:
logger.warning(f"Dado inválido no pedido {pedido_id}: {e}")
return False
except Exception as e:
logger.exception(f"Erro inesperado ao processar pedido {pedido_id}")
return False
processar_pedido(42)
processar_pedido(-1)
O método logger.exception() registra automaticamente o traceback completo, o que é essencial para depuração.
Boas práticas no tratamento de erros
Para escrever código robusto e profissional:
- Seja específico: capture exceções específicas, não
Exceptiongenérico. - Não silencie erros: evite blocos
exceptvazios compass. Se capturou, faça algo com o erro. - Use else e finally: o bloco
elsemantém o try enxuto, e ofinallygarante limpeza de recursos. - Crie exceções personalizadas: para projetos médios e grandes, exceções customizadas melhoram a clareza.
- Documente exceções: nas docstrings das funções, liste as exceções que podem ser levantadas.
- Registre erros: use
loggingem vez deprintpara produção. - Falhe cedo: valide dados na entrada, não no meio do processamento.
Conclusão
O tratamento de erros é uma habilidade fundamental que separa código amador de código profissional. Python oferece ferramentas poderosas para lidar com exceções de forma elegante e eficiente. Dominar try/except, criar exceções personalizadas e seguir boas práticas de logging garantem que suas aplicações sejam robustas e fáceis de manter.
Como próximos passos, estude o módulo logging em profundidade, explore decoradores para tratamento de erros reutilizável e aprenda sobre validação de dados com bibliotecas como Pydantic, que automatizam grande parte da validação em projetos modernos.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português