Scripts de análise de dados que rodam uma vez em modo interativo toleram erros — você vê o problema e corrige. Scripts em produção (rodando automaticamente, gerando relatórios, enviando dados) precisam tratar erros graciosamente: registrar o problema, continuar onde possível e notificar quando necessário.

Estrutura básica: try/except

# Sem tratamento (para ao primeiro erro):
valor = int("abc")  # ValueError!

# Com tratamento:
try:
    valor = int("abc")
except ValueError:
    print("Valor inválido — não é um número")
    valor = 0

Capturando tipos específicos de erro

try:
    df = pd.read_csv(caminho)
    resultado = df["valor"].sum()
except FileNotFoundError:
    print(f"Arquivo não encontrado: {caminho}")
except KeyError as e:
    print(f"Coluna ausente no arquivo: {e}")
except Exception as e:
    # Captura qualquer outro erro não previsto:
    print(f"Erro inesperado: {type(e).__name__}: {e}")

Seja específico nas exceções — não use except Exception para tudo. Capturar exceções específicas força você a pensar nos erros possíveis.

else e finally

try:
    df = pd.read_csv("dados.csv")
except FileNotFoundError:
    print("Arquivo não encontrado")
else:
    # Executado APENAS se não houve exceção:
    print(f"Arquivo carregado: {len(df)} linhas")
    processar(df)
finally:
    # Executado SEMPRE (com ou sem erro):
    print("Processamento concluído")

finally é ideal para fechar conexões de banco, liberar arquivos abertos ou registrar o fim de um processo.

Logging: registrando erros em vez de apenas imprimir

import logging

logging.basicConfig(
    filename="processamento.log",
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)

try:
    df = pd.read_csv("dados.csv")
    logging.info(f"Arquivo carregado: {len(df)} linhas")
except FileNotFoundError:
    logging.error("Arquivo dados.csv não encontrado")
except Exception as e:
    logging.exception(f"Erro não tratado: {e}")

logging.exception() registra o erro com o traceback completo — muito mais útil que print() para depurar problemas em produção.

raise: levantando exceções intencionalmente

def calcula_media(valores):
    if not valores:
        raise ValueError("A lista de valores não pode ser vazia")
    if not all(isinstance(v, (int, float)) for v in valores):
        raise TypeError("Todos os valores devem ser numéricos")
    return sum(valores) / len(valores)

try:
    media = calcula_media([])
except ValueError as e:
    print(f"Erro: {e}")

Perguntas frequentes

Devo usar assert para validar dados?

Não em código de produção. assert pode ser desabilitado com a flag -O (otimização) do Python. Use raise com exceções explícitas para validações importantes.

Como lidar com erros em loops sem interromper todo o processamento?

Coloque o try/except dentro do loop: for arquivo in arquivos: try: processar(arquivo) except Exception as e: logging.error(f"{arquivo}: {e}"); continue. O loop continua nos demais itens mesmo quando um falha.