Curso de Python / Usar decoradores e gerenciadores de contexto com limpeza segura
Um decorador envolve a chamada de uma função; um gerenciador de contexto organiza a preparação e a limpeza de um bloco with. Use essas ferramentas para separar tarefas repetidas, preservando de forma intencional os argumentos, o valor retornado e a propagação das exceções.
Distinguir o decorador do gerenciador de contexto
A sintaxe @announce faz Python passar a função recém-definida para announce e associar o nome ao objeto retornado. Para este exemplo, equivale a definir a função e depois executar total = announce(total). O decorador não é um comentário: ele troca o objeto chamado quando você usa esse nome.
Já with delimita a utilização de um recurso. Um gerenciador de contexto executa a entrada, entrega um valor ao bloco e trata a saída. A função marcada com @contextmanager deve fornecer exatamente um valor por yield. O código antes desse ponto prepara o recurso; quando o bloco termina, a execução retorna ao gerador.
functools.wraps preserva metadados úteis, como nome e documentação da função original, e mantém a referência __wrapped__. Ele não verifica os tipos dos argumentos nem garante que o decorador tenha preservado o resultado e as regras de erro. Consulte a documentação de functools.wraps para os metadados tratados.
Executar o exemplo completo de função e lista temporária
Crie uma pasta de prática e salve o arquivo wrappers.py em UTF-8 com todo o conteúdo abaixo. O exemplo usa apenas a biblioteca padrão; nenhuma instalação com pip é necessária. No terminal do ambiente virtual do projeto, execute python wrappers.py. Fora de um ambiente virtual no macOS ou Ubuntu, use python3 wrappers.py se esse for o comando do seu interpretador.
from functools import wraps
from contextlib import contextmanager
def announce(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("start")
return func(*args, **kwargs)
return wrapper
@contextmanager
def temporary_items():
items = []
try:
yield items
finally:
items.clear()
print("cleared")
@announce
def total(amounts):
return sum(amounts)
with temporary_items() as items:
items.extend([3500, 8000])
print(total(items))
print(total.__name__)
print(items)
O resultado esperado é:
start
11500
cleared
total
[]
temporary_items() cria uma lista vazia, que o as items disponibiliza no bloco. Depois de adicionar os dois valores, a chamada de total(items) passa pelo wrapper: primeiro imprime start, depois retorna a soma, que o print externo mostra como 11500.
Na saída do bloco, finally limpa essa mesma lista e imprime cleared. A variável items continua acessível depois do with; ela aponta para a lista agora vazia. O bloco não cria um novo escopo de variáveis. O nome total mostrado na penúltima linha vem dos metadados preservados por wraps.
Acompanhar retornos e exceções sem ocultar falhas
O return func(*args, **kwargs) repassa argumentos posicionais e nomeados e devolve o resultado original. Se você deixar apenas func(*args, **kwargs), a soma ainda será calculada, mas o wrapper terminará sem devolver esse valor: o chamador receberá None. Uma mensagem de início correta não basta para provar que o decorador funciona.
Se a função original lançar uma exceção, este wrapper também a propaga, pois não a captura. O gerenciador recebe a exceção no ponto do yield, e seu finally ainda executa a limpeza. A linha depois do with só será alcançada se a exceção for tratada em um nível apropriado; a limpeza não transforma automaticamente uma falha em sucesso.
Capturar uma exceção ao redor do yield e não a relançar pode indicar que ela foi tratada, permitindo continuar depois do bloco. Se você apenas registrar o erro, use raise para propagá-lo. A documentação de contextmanager explica esse contrato de entrada, saída e exceção. Os fundamentos estão na aula de tratamento de exceções.
Testar a limpeza e escolher quando abstrair
- No exemplo original, altere somente
8000para9000. A soma deve passar para12500, mantendo a limpeza e a lista final vazia. Restaure8000. - Dentro do bloco, acrescente
raise ValueError("teste")logo depois deitems.extend(...). A execução deve imprimirclearede terminar com a exceção; as impressões posteriores não serão executadas. Para inspecionar a lista nesse caso, coloque o bloco inteiro em umtrye captureValueErrordepois dele, imprimindoitemsno tratamento. Espere[]. Não mova a limpeza para fora dofinally. - Remova o teste de erro, tire temporariamente apenas o
returndo wrapper e execute novamente. A segunda linha deve serNone. Recoloque oreturne confirme o resultado original completo.
Essa lista ilustra a duração controlada de um recurso; esvaziá-la não é o mesmo que fechar um arquivo ou desfazer uma transação. Cada recurso exige a operação de limpeza adequada. Prefira o suporte nativo a with quando o objeto já o oferece, como arquivos abertos com open.
Use um decorador quando o comportamento de chamada realmente se repetir e um gerenciador quando houver uma duração clara de uso. Não acrescente tentativas automáticas de repetição apenas por conveniência: repetir uma escrita pode criar registros ou operações duplicadas. Uma tarefa pequena usada uma única vez pode continuar mais clara com um try/finally local.




