Context Managers Personalizados em Python: Controle Avançado com __enter__ e __exit__
with
, como em arquivos:with open("arquivo.txt", "r") as f:
conteudo = f.read()
Por trás disso, o Python usa métodos especiais __enter__
e __exit__
. Criar context managers personalizados permite estender essa funcionalidade para conexões de banco, locks, logs, recursos externos e qualquer cenário que precise de setup/teardown seguro.
1. Conceito de Context Manager
Um context manager é uma classe que implementa os métodos mágicos:
__enter__(self)
: Executado no início do blocowith
. Pode retornar um objeto que será usado dentro dowith
.__exit__(self, exc_type, exc_value, traceback)
: Executado ao final do blocowith
. Recebe informações sobre exceções, se houver.
Estrutura básica:
class MeuContexto:
def __enter__(self):
print("Entrando no contexto")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Saindo do contexto")
if exc_type:
print(f"Exceção capturada: {exc_value}")
return False # False propaga a exceção
Uso:
with MeuContexto():
print("Dentro do bloco")
# raise ValueError("Teste de exceção")
Saída:
Entrando no contexto
Dentro do bloco
Saindo do contexto
Exceção capturada: Teste de exceção # Se houver
2. Criando Context Managers Personalizados
2.1 Exemplo: Temporizador de execução
import time
class Temporizador:
def __enter__(self):
self.inicio = time.time()
return self
def __exit__(self, exc_type, exc_value, traceback):
self.fim = time.time()
print(f"Tempo de execução: {self.fim - self.inicio:.4f} segundos")
with Temporizador():
total = sum(range(1000000))
Útil para benchmarking de trechos de código.
2.2 Exemplo: Gerenciamento de Arquivos
Embora Python já tenha open
, podemos criar um context manager personalizado que adiciona logs e tratamento de erros:
class GerenciadorArquivo:
def __init__(self, arquivo, modo):
self.arquivo = arquivo
self.modo = modo
self.obj = None
def __enter__(self):
print(f"Abrindo {self.arquivo}")
self.obj = open(self.arquivo, self.modo)
return self.obj
def __exit__(self, exc_type, exc_value, traceback):
print(f"Fechando {self.arquivo}")
self.obj.close()
if exc_type:
print(f"Exceção: {exc_value}")
return False # Propaga exceção
with GerenciadorArquivo("teste.txt", "w") as f:
f.write("Conteúdo de teste")
# raise Exception("Erro proposital")
Saída:
Abrindo teste.txt
Fechando teste.txt
Exceção: Erro proposital # Se exceção ocorrer
2.3 Exemplo: Locks e Recursos Compartilhados
Em sistemas multithread, context managers podem garantir aquisição e liberação de locks:
import threading
lock = threading.Lock()
class LockContext:
def __enter__(self):
print("Adquirindo lock")
lock.acquire()
return lock
def __exit__(self, exc_type, exc_value, traceback):
lock.release()
print("Lock liberado")
return False
with LockContext():
print("Seção crítica executando")
Garante que locks sejam liberados mesmo em caso de erro.
3. Context Managers Parametrizados
Podemos criar context managers que aceitam parâmetros na inicialização, aumentando flexibilidade:
class Mensagem:
def __init__(self, texto, repetir=1):
self.texto = texto
self.repetir = repetir
def __enter__(self):
for _ in range(self.repetir):
print(f"Início: {self.texto}")
return self
def __exit__(self, exc_type, exc_value, traceback):
print(f"Fim: {self.texto}")
return False
with Mensagem("Processando", repetir=3):
print("Dentro do bloco")
Saída:
Início: Processando
Início: Processando
Início: Processando
Dentro do bloco
Fim: Processando
4. Boas Práticas com Context Managers
Sempre garanta liberação de recursos no
__exit__
.Propague exceções corretamente a menos que queira silenciá-las intencionalmente.
Use context managers para setup/teardown: logs, conexões de banco, locks, arquivos, sessões web.
Prefira
contextlib
para simplificar:@contextlib.contextmanager
permite criar context managers via geradores, evitando classes completas.
Exemplo com contextlib
:
from contextlib import contextmanager
@contextmanager
def temporizador():
import time
inicio = time.time()
yield
fim = time.time()
print(f"Tempo: {fim - inicio:.4f} segundos")
with temporizador():
total = sum(range(1000000))
5. Aplicações Avançadas
Gerenciamento de conexões a bancos de dados, garantindo commit/rollback.
Temporizadores para análise de performance em produção.
Locks e sincronização em sistemas concorrentes.
Controle de contexto em frameworks web (sessões, autenticação).
Mocking e testes: criar contextos temporários de dados.
6. Conclusão
Context Managers personalizados são essenciais para gerenciar recursos de forma segura e elegante.
Com
__enter__
e__exit__
, você tem controle total sobre setup, teardown e tratamento de exceções.Devem ser usados sempre que houver recursos que precisam ser abertos, usados e fechados de forma previsível.
Combinados com decorators, mixins e POO avançada, tornam o código robusto, legível e seguro para produção.
Comentários
Postar um comentário