Python Try Except: Como tratar exceções da maneira correta
Updated on
Seu script Python lê um arquivo, analisa os dados e os envia para uma API. Funciona perfeitamente na sua máquina. Então ele roda em um servidor onde o caminho do arquivo está errado, o JSON está malformado ou a rede está fora do ar. O programa trava com um traceback e todo o pipeline para. Este é o problema que o try/except resolve. Em vez de deixar erros matarem seu programa, você os captura, trata e continua executando.
O que são exceções em Python?
Uma exceção é um evento que interrompe o fluxo normal de um programa. Quando o Python encontra uma operação que não pode realizar -- dividir por zero, acessar uma chave de dicionário inexistente, abrir um arquivo que não existe -- ele cria um objeto de exceção e para a execução. Se nada capturar essa exceção, o programa termina e exibe um traceback.
# This crashes the program
result = 10 / 0Saída:
Traceback (most recent call last):
File "example.py", line 2, in <module>
result = 10 / 0
ZeroDivisionError: division by zeroExceções são diferentes de erros de sintaxe. Um erro de sintaxe significa que o Python não consegue analisar seu código de forma alguma. Uma exceção acontece durante a execução, após o código ter sido analisado com sucesso.
Sintaxe básica do Try/Except
O bloco try/except permite que você tente código que pode falhar e defina o que acontece se ele falhar.
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero.")Saída:
Cannot divide by zero.O programa não trava. O Python executa o código dentro do try. Quando ZeroDivisionError ocorre, a execução pula para o bloco except. Tudo após o try/except continua normalmente.
Você também pode capturar o objeto de exceção para inspecionar a mensagem de erro:
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"Error: {e}")Saída:
Error: division by zeroCapturando exceções específicas
O Python tem dezenas de tipos de exceções integradas. Capturar o tipo correto torna seu código preciso. Aqui estão as exceções mais comuns que você vai tratar.
ValueError
Levantada quando uma função recebe um argumento com o tipo correto mas um valor inadequado.
try:
number = int("not_a_number")
except ValueError as e:
print(f"Invalid value: {e}")Saída:
Invalid value: invalid literal for int() with base 10: 'not_a_number'TypeError
Levantada quando uma operação é aplicada a um objeto do tipo errado.
try:
result = "hello" + 42
except TypeError as e:
print(f"Type error: {e}")Saída:
Type error: can only concatenate str (not "int") to strFileNotFoundError
Levantada quando você tenta abrir um arquivo que não existe.
try:
with open("nonexistent_file.txt", "r") as f:
content = f.read()
except FileNotFoundError:
print("File not found. Check the file path.")KeyError
Levantada quando você acessa uma chave de dicionário que não existe.
data = {"name": "Alice", "age": 30}
try:
email = data["email"]
except KeyError as e:
print(f"Missing key: {e}")Saída:
Missing key: 'email'IndexError
Levantada quando você acessa um índice de lista fora do intervalo.
items = [10, 20, 30]
try:
value = items[5]
except IndexError:
print("Index out of range.")Múltiplos blocos Except
Você pode tratar diferentes tipos de exceções com blocos except separados. O Python os verifica em ordem e executa a primeira correspondência.
def parse_config(raw_value):
try:
parts = raw_value.split(":")
key = parts[0]
value = int(parts[1])
return {key: value}
except IndexError:
print("Config format error: missing colon separator.")
except ValueError:
print("Config format error: value is not a number.")
except AttributeError:
print("Config format error: input is not a string.")
parse_config("timeout:30") # Returns {'timeout': 30}
parse_config("timeout") # Config format error: missing colon separator.
parse_config("timeout:abc") # Config format error: value is not a number.
parse_config(12345) # Config format error: input is not a string.Você também pode capturar múltiplas exceções em um único bloco except usando uma tupla:
try:
value = int(input("Enter a number: "))
result = 100 / value
except (ValueError, ZeroDivisionError) as e:
print(f"Invalid input: {e}")O bloco Else
O bloco else é executado apenas quando nenhuma exceção ocorre no bloco try. Ele separa o código do "caminho feliz" do código de tratamento de erros.
try:
number = int("42")
except ValueError:
print("That is not a valid number.")
else:
print(f"Successfully parsed: {number}")Saída:
Successfully parsed: 42O bloco else é útil porque qualquer exceção levantada dentro dele não é capturada pelos blocos except anteriores. Isso evita silenciar acidentalmente bugs no seu código de caminho de sucesso.
filename = "data.txt"
try:
f = open(filename, "r")
except FileNotFoundError:
print(f"File '{filename}' does not exist.")
else:
content = f.read()
f.close()
print(f"Read {len(content)} characters.")Se open() falhar, o bloco except trata isso. Se open() tiver sucesso, o bloco else lê o arquivo. Qualquer erro durante f.read() não é capturado aqui -- ele se propaga para cima, que é o comportamento correto.
O bloco Finally
O bloco finally sempre é executado, tenha ocorrido uma exceção ou não. É o lugar certo para código de limpeza: fechar arquivos, liberar bloqueios, desconectar de bancos de dados.
f = None
try:
f = open("data.txt", "r")
content = f.read()
except FileNotFoundError:
print("File not found.")
finally:
if f is not None:
f.close()
print("File handle closed.")O bloco finally é executado mesmo se o bloco try retornar um valor ou levantar uma exceção não tratada.
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
return None
finally:
print("Division attempted.")
result = divide(10, 3)
# Output: Division attempted.
# result = 3.3333333333333335
result = divide(10, 0)
# Output: Division attempted.
# result = NoneO padrão completo Try/Except/Else/Finally
Veja como os quatro blocos funcionam juntos:
import json
def load_config(filepath):
"""Load and parse a JSON config file."""
f = None
try:
f = open(filepath, "r")
data = json.load(f)
except FileNotFoundError:
print(f"Config file '{filepath}' not found.")
return {}
except json.JSONDecodeError as e:
print(f"Invalid JSON in '{filepath}': {e}")
return {}
else:
print(f"Config loaded successfully with {len(data)} keys.")
return data
finally:
if f is not None:
f.close()
print("File handle released.")
config = load_config("settings.json")| Bloco | Quando é executado | Propósito |
|---|---|---|
try | Sempre (primeiro) | Contém o código que pode levantar uma exceção |
except | Somente quando uma exceção correspondente ocorre | Trata o erro |
else | Somente quando nenhuma exceção ocorre no try | Executa a lógica de sucesso |
finally | Sempre (último) | Código de limpeza que deve ser executado independentemente |
Referência de exceções integradas comuns
O Python inclui uma hierarquia de exceções integradas. Aqui estão as que você vai encontrar com mais frequência.
| Exceção | Quando ocorre | Exemplo |
|---|---|---|
Exception | Classe base para a maioria das exceções | Pai de todas as exceções não-system-exiting |
ValueError | Valor errado para o tipo correto | int("abc") |
TypeError | Tipo errado para uma operação | "text" + 5 |
KeyError | Chave de dicionário ausente | d["missing"] |
IndexError | Índice de lista fora do intervalo | [1,2,3][10] |
FileNotFoundError | Arquivo não existe | open("no.txt") |
ZeroDivisionError | Divisão ou módulo por zero | 1 / 0 |
AttributeError | Objeto não tem o atributo | None.append(1) |
ImportError | Importação de módulo falha | import nonexistent |
OSError | Operação do SO falha | Disco cheio, permissão negada |
StopIteration | Iterador não tem mais itens | next() em iterador esgotado |
RuntimeError | Erro genérico de tempo de execução | Genérico para falhas diversas |
Todas estas herdam de BaseException. Na prática, você deve capturar Exception ou suas subclasses, nunca BaseException diretamente (que inclui SystemExit e KeyboardInterrupt).
Levantando exceções com raise
Você pode levantar exceções explicitamente quando seu código detecta condições inválidas. É assim que você impõe precondições e sinaliza erros ao chamador.
def set_age(age):
if not isinstance(age, int):
raise TypeError(f"Age must be an integer, got {type(age).__name__}")
if age < 0 or age > 150:
raise ValueError(f"Age must be between 0 and 150, got {age}")
return age
# Valid usage
print(set_age(25)) # 25
# Invalid usage
try:
set_age(-5)
except ValueError as e:
print(e) # Age must be between 0 and 150, got -5
try:
set_age("thirty")
except TypeError as e:
print(e) # Age must be an integer, got strVocê também pode relevantar a exceção atual dentro de um bloco except usando raise sem argumentos. Isso é útil quando você quer registrar um erro e depois deixá-lo se propagar.
import logging
try:
result = 10 / 0
except ZeroDivisionError:
logging.error("Division by zero encountered")
raise # re-raises the original ZeroDivisionErrorClasses de exceções personalizadas
Para projetos maiores, defina suas próprias classes de exceções. Exceções personalizadas tornam o tratamento de erros mais legível e permitem que os chamadores capturem modos de falha específicos.
class ValidationError(Exception):
"""Raised when input data fails validation."""
def __init__(self, field, message):
self.field = field
self.message = message
super().__init__(f"Validation failed on '{field}': {message}")
class DatabaseConnectionError(Exception):
"""Raised when the database connection fails."""
pass
# Usage
def validate_email(email):
if "@" not in email:
raise ValidationError("email", "Must contain @ symbol")
if "." not in email.split("@")[1]:
raise ValidationError("email", "Domain must contain a dot")
return email
try:
validate_email("userexample.com")
except ValidationError as e:
print(e) # Validation failed on 'email': Must contain @ symbol
print(e.field) # email
print(e.message) # Must contain @ symbolExceções personalizadas devem herdar de Exception, não de BaseException. Agrupe exceções relacionadas sob uma classe base comum para que os chamadores possam capturar categorias amplas ou específicas:
class AppError(Exception):
"""Base exception for this application."""
pass
class ConfigError(AppError):
pass
class NetworkError(AppError):
pass
# Caller can catch all app errors or specific ones
try:
raise NetworkError("Connection timed out")
except AppError as e:
print(f"Application error: {e}")Melhores práticas para tratamento de exceções em Python
1. Nunca capture exceções genéricas
Um except: genérico captura tudo, incluindo KeyboardInterrupt e SystemExit. Isso esconde bugs e torna a depuração um pesadelo.
# BAD - catches everything, hides real bugs
try:
do_something()
except:
pass
# GOOD - catches specific exceptions
try:
do_something()
except ValueError as e:
logging.warning(f"Invalid value: {e}")Se você precisa capturar uma ampla gama, use except Exception em vez disso. Isso ainda permite que KeyboardInterrupt e SystemExit se propaguem.
2. Mantenha os blocos Try pequenos
Coloque apenas o código que pode levantar a exceção dentro do bloco try. Blocos try grandes tornam difícil identificar qual linha causou o erro.
# BAD - too much code in try
try:
data = load_data()
cleaned = clean_data(data)
result = analyze(cleaned)
save_results(result)
except Exception as e:
print(f"Something failed: {e}")
# GOOD - narrow try blocks
data = load_data()
cleaned = clean_data(data)
try:
result = analyze(cleaned)
except ValueError as e:
print(f"Analysis failed: {e}")
result = default_result()
save_results(result)3. Registre as exceções, não as silencie
Engolir exceções com pass cria bugs invisíveis. Sempre registre ou reporte o erro.
import logging
try:
process_record(record)
except ValueError as e:
logging.error(f"Failed to process record {record['id']}: {e}")4. Use tipos de exceções específicos
Capture o tipo de exceção mais específico possível. Isso evita tratar acidentalmente erros que você não antecipou.
| Abordagem | Captura | Nível de risco |
|---|---|---|
except: | Tudo incluindo SystemExit | Muito alto |
except Exception: | Todas as exceções padrão | Alto |
except ValueError: | Apenas ValueError | Baixo |
except (ValueError, TypeError): | Dois tipos específicos | Baixo |
5. Limpe recursos no Finally ou use gerenciadores de contexto
Para manipuladores de arquivo, conexões de banco de dados e bloqueios, sempre use finally ou (melhor ainda) uma instrução with.
# Prefer context managers for resource cleanup
with open("data.txt", "r") as f:
content = f.read()
# File is automatically closed, even if an exception occursExemplos do mundo real
Lendo e analisando um arquivo JSON
import json
def read_json_config(filepath):
"""Read a JSON configuration file with proper error handling."""
try:
with open(filepath, "r") as f:
config = json.load(f)
except FileNotFoundError:
print(f"Config file not found: {filepath}")
return None
except PermissionError:
print(f"No permission to read: {filepath}")
return None
except json.JSONDecodeError as e:
print(f"Invalid JSON at line {e.lineno}, column {e.colno}: {e.msg}")
return None
else:
print(f"Loaded config with keys: {list(config.keys())}")
return config
config = read_json_config("app_config.json")
if config:
db_host = config.get("database_host", "localhost")Fazendo chamadas de API HTTP
import urllib.request
import urllib.error
import json
def fetch_user(user_id):
"""Fetch user data from an API with retry logic."""
url = f"https://jsonplaceholder.typicode.com/users/{user_id}"
max_retries = 3
for attempt in range(1, max_retries + 1):
try:
with urllib.request.urlopen(url, timeout=5) as response:
data = json.loads(response.read().decode())
return data
except urllib.error.HTTPError as e:
if e.code == 404:
print(f"User {user_id} not found.")
return None
print(f"HTTP error {e.code} on attempt {attempt}")
except urllib.error.URLError as e:
print(f"Network error on attempt {attempt}: {e.reason}")
except json.JSONDecodeError:
print("API returned invalid JSON.")
return None
print(f"All {max_retries} attempts failed.")
return None
user = fetch_user(1)
if user:
print(f"Found user: {user['name']}")Processando dados CSV
import csv
def process_sales_data(filepath):
"""Process a CSV file with robust error handling."""
results = []
try:
with open(filepath, "r", newline="") as f:
reader = csv.DictReader(f)
for row_num, row in enumerate(reader, start=2):
try:
amount = float(row["amount"])
quantity = int(row["quantity"])
results.append({
"product": row["product"],
"total": amount * quantity,
})
except KeyError as e:
print(f"Row {row_num}: Missing column {e}")
except ValueError as e:
print(f"Row {row_num}: Invalid number - {e}")
except FileNotFoundError:
print(f"File not found: {filepath}")
except PermissionError:
print(f"Cannot read file: {filepath}")
return resultsTry/Except vs If/Else: Quando usar cada um
O Python segue o princípio EAFP: "Easier to Ask Forgiveness than Permission" (É mais fácil pedir perdão do que permissão). Isso contrasta com a abordagem LBYL: "Look Before You Leap" (Olhe antes de pular).
# LBYL (Look Before You Leap) - using if/else
if "email" in user_data:
email = user_data["email"]
else:
email = "unknown"
# EAFP (Easier to Ask Forgiveness) - using try/except
try:
email = user_data["email"]
except KeyError:
email = "unknown"| Critério | if/else (LBYL) | try/except (EAFP) |
|---|---|---|
| Melhor quando | A verificação é barata e o erro é comum | O erro é raro ou a verificação é cara |
| Desempenho (sem erro) | Ligeiramente mais lento (verificação extra toda vez) | Ligeiramente mais rápido (sem overhead de verificação) |
| Desempenho (erro ocorre) | Igual | Mais lento (criação de exceção tem overhead) |
| Condições de corrida | Possíveis (estado pode mudar entre verificação e uso) | Nenhuma (operação atômica) |
| Legibilidade | Clara para condições simples | Melhor para operações que podem falhar de múltiplas formas |
| Operações de arquivo | if os.path.exists(path) -- arquivo pode ser excluído entre verificação e abertura | try: open(path) -- trata a falha real |
| Acesso a dicionário | if key in dict -- simples e rápido | try: dict[key] -- ou simplesmente use dict.get(key, default) |
Use try/except quando:
- O caso de falha é raro (exceções são otimizadas para o caminho "sem erro").
- A verificação em si é tão cara quanto a operação (por exemplo, verificar se um arquivo existe e depois abri-lo).
- Múltiplas coisas podem dar errado em uma sequência de operações.
- Condições de corrida são uma preocupação (especialmente com arquivos e recursos de rede).
Use if/else quando:
- A condição é barata para verificar e falhas são comuns.
- Você está validando a entrada do usuário antes de processar.
- A lógica fica mais clara como um condicional.
Depurando exceções mais rápido com RunCell
Quando você trabalha em notebooks Jupyter, exceções podem quebrar seu fluxo de análise. Você encontra um KeyError na linha 50.000 de um DataFrame, ou um TypeError aparece três células dentro de um pipeline. Rastrear a causa raiz significa rolar tracebacks e inspecionar variáveis manualmente.
RunCell (opens in a new tab) é um agente de IA que roda diretamente dentro do Jupyter. Ele lê o traceback completo, inspeciona as variáveis no seu escopo atual e sugere uma correção em contexto. Veja como ele ajuda com o tratamento de exceções:
- Análise de traceback. RunCell analisa a cadeia de exceções e identifica qual variável ou operação causou a falha, mesmo em chamadas de funções aninhadas.
- Sugestões de correção. Em vez de procurar no Stack Overflow, RunCell gera uma célula de código corrigida que você pode executar imediatamente. Ele sabe se deve adicionar um
try/except, corrigir uma conversão de tipo ou tratar uma chave ausente. - Verificações preventivas. RunCell pode escanear seu código e sinalizar operações que provavelmente vão levantar exceções -- como acessar chaves de dicionário sem
.get(), ou dividir sem verificar zero -- antes de você executar a célula.
Como o RunCell opera dentro do seu ambiente Jupyter existente, ele tem acesso aos seus dados e variáveis reais. As sugestões que ele fornece são específicas para a sua situação, não conselhos genéricos.
FAQ
Qual é a diferença entre try/except e try/catch em Python?
O Python usa try/except, não try/catch. A sintaxe try/catch pertence a linguagens como Java, JavaScript e C++. Em Python, a palavra-chave é except. A funcionalidade é a mesma: você tenta código que pode falhar e define um tratador para o caso de falha.
Posso usar múltiplos blocos except em Python?
Sim. Você pode encadear quantos blocos except precisar, cada um capturando um tipo de exceção diferente. O Python os avalia em ordem e executa o primeiro bloco correspondente. Você também pode capturar múltiplas exceções em um bloco usando uma tupla: except (ValueError, TypeError) as e:.
Quando devo usar else com try/except?
Use o bloco else quando você tem código que só deve ser executado se o bloco try teve sucesso. O principal benefício é que exceções levantadas no bloco else não são capturadas pelos blocos except anteriores, evitando que você silencie acidentalmente erros não relacionados.
O finally sempre é executado em Python?
Sim. O bloco finally é executado independentemente de o bloco try ter completado normalmente, levantado uma exceção tratada ou levantado uma exceção não tratada. Ele é executado mesmo se o bloco try ou except contiver uma instrução return. As únicas exceções são se o processo Python for encerrado externamente ou os._exit() for chamado.
Como criar exceções personalizadas em Python?
Crie uma nova classe que herda de Exception. Você pode adicionar atributos personalizados e sobrescrever o método __init__. Por exemplo: class MyError(Exception): pass. Para casos mais complexos, adicione campos como códigos de erro ou dados de contexto. Sempre herde de Exception, não de BaseException.
Conclusão
O try/except do Python é o mecanismo padrão para tratar erros de tempo de execução. Ele permite capturar exceções específicas, executar código de limpeza e manter seus programas estáveis quando as coisas dão errado. O padrão completo -- try/except/else/finally -- cobre cada cenário: tentar a operação, tratar falhas, executar lógica de sucesso e limpar recursos.
Os princípios-chave são diretos. Capture exceções específicas, não amplas. Mantenha seus blocos try pequenos. Sempre registre ou reporte erros em vez de silenciá-los. Use finally ou gerenciadores de contexto para limpeza. Levante exceções significativas no seu próprio código com mensagens de erro claras.
Seja lendo arquivos, fazendo chamadas de API ou processando entrada de usuário, o tratamento adequado de exceções é a diferença entre um script que trava às 2 da manhã e um que registra o erro e continua executando. Comece com o básico -- um simples try/except em volta de código que pode falhar -- e evolua para hierarquias de exceções personalizadas à medida que seus projetos crescem.