Módulo 6 - Context managers e o with
O atalho: @contextlib.contextmanager
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Usar @contextlib.contextmanager para criar um gerenciador com uma função.
- Entender que o código antes do yield é a entrada e o depois é a saída.
- Colocar a limpeza em um finally para cobrir exceções.
- Escolher entre a classe e a função com yield conforme o caso.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: O atalho: @contextlib.contextmanager.
Os objetivos desta aula. Usar @contextlib.contextmanager para criar um gerenciador com uma função. Entender que o código antes do yield é a entrada e o depois é a saída. Colocar a limpeza em um finally para cobrir exceções. Escolher entre a classe e a função com yield conforme o caso.
Veja o essencial, parte por parte.
Um context manager em uma função. @contextlib.contextmanager transforma uma função com yield em context manager.
Por que a limpeza vai no finally. Sempre coloque o yield dentro de um try, com a limpeza no finally.
Classe ou função: quando usar cada uma. Com as duas formas na mão, surge a pergunta: qual usar?
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Um context manager em uma função
A classe com __enter__ e __exit__ é poderosa, mas exagerada para casos simples. O módulo contextlib traz um atalho elegante: o decorador contextmanager. Você escreve uma função geradora, aquelas com yield, e o decorador a transforma em um context manager. A leitura fica natural: tudo que vem antes do yield é a preparação, equivalente ao __enter__; o valor que você cede com yield é o que vai para o as; e tudo que vem depois do yield é a limpeza, equivalente ao __exit__.
from contextlib import contextmanager
@contextmanager
def conexao(host):
print(f"conectando a {host}") # entrada (__enter__)
conn = {"host": host, "ativa": True}
try:
yield conn # o valor vai para o as
finally:
conn["ativa"] = False
print(f"desconectando de {host}") # saida (__exit__)
with conexao("servidor") as c:
print(f"usando, ativa = {c['ativa']}")
# conectando a servidor
# usando, ativa = True
# desconectando de servidorAntes do yield é a entrada; depois do yield, dentro do finally, é a limpeza.
Compare com a classe Conexao da aula anterior: o comportamento é idêntico, mas o código cabe em menos linhas e se lê de cima para baixo como uma pequena história. Por isso, na prática do dia a dia, essa é a forma mais comum de criar context managers próprios. A classe fica reservada para quando você precisa guardar bastante estado ou reaproveitar o objeto em contextos diferentes.
Por que a limpeza vai no finally
Há um detalhe que faz toda a diferença: envolver o yield em um try com finally. Quando ocorre uma exceção dentro do bloco with, o Python a repassa para dentro da função geradora, no ponto do yield, como se ela tivesse acontecido ali. Se a limpeza estivesse solta depois do yield, sem o finally, essa exceção pularia a limpeza, exatamente o problema que o with deveria resolver. Colocar a limpeza no finally garante que ela rode aconteça o que acontecer, fiel à promessa do with.
from contextlib import contextmanager
@contextmanager
def recurso():
print("adquire")
try:
yield
finally:
print("libera") # roda mesmo se o bloco lancar excecao
try:
with recurso():
print("usando")
raise RuntimeError("falhou")
except RuntimeError:
print("erro tratado fora")
# adquire
# usando
# libera <- a limpeza rodou apesar do erro
# erro tratado foraO finally garante a limpeza mesmo quando o bloco with lança uma exceção.
Classe ou função: quando usar cada uma
Com as duas formas na mão, surge a pergunta: qual usar? A resposta é sobre estado e reaproveitamento. Se o gerenciador é simples, faz uma preparação e uma limpeza e pronto, a função com yield é mais curta e clara, e deve ser a primeira escolha. Se o gerenciador guarda bastante estado, tem métodos próprios ou precisa ser reutilizado como objeto em vários lugares, a classe com __enter__ e __exit__ organiza melhor. As duas produzem context managers equivalentes; muda a ergonomia.
Função com @contextmanager
- Ideal para casos simples e diretos
- Menos código, leitura de cima para baixo
- Preparação e limpeza numa função só
- A forma mais comum no dia a dia
Classe com __enter__/__exit__
- Ideal quando há bastante estado a guardar
- Permite métodos próprios no objeto
- Reutilizável como objeto em vários blocos
- Mais verbosa, porém mais estruturada
Uma boa heurística: comece pela função com yield e só migre para a classe se sentir falta de estrutura. O contextlib ainda traz utilitários que combinam com essa abordagem, como o suppress e o ExitStack, que a próxima aula apresenta. O importante é que, tendo entendido o protocolo __enter__ e __exit__ e o atalho com yield, você já consegue criar gerenciadores de contexto para praticamente qualquer recurso com começo e fim.
Teste rápido
Em um context manager criado com @contextmanager, o que representa o código depois do yield?
Perguntas frequentes
- Por que a função precisa de exatamente um yield?
- Porque o contextmanager usa o yield como a fronteira entre entrada e saída: uma pausa única onde o bloco with roda. Zero yields não define essa fronteira, e mais de um faria o gerador ceder controle duas vezes, o que o decorador rejeita com um erro. A estrutura é sempre: preparar, um yield, limpar.
- O que acontece com a exceção do bloco dentro da função geradora?
- Ela é injetada no ponto do yield, como se tivesse ocorrido ali. Por isso, se você quiser reagir ao erro, envolve o yield com try, except; e para garantir a limpeza sempre, usa o finally. Se você não capturar, a exceção continua subindo normalmente depois que a limpeza rodar.
- Preciso sempre ceder um valor com o yield?
- Não. Se o bloco with não precisa de nenhum objeto, você pode escrever só yield, sem valor, e usar with recurso(): sem o as. O gerenciador ainda faz a preparação e a limpeza; ele só não entrega nada para o bloco usar. É comum em gerenciadores que mudam um estado global temporariamente.
- A função com yield vira um gerador de verdade?
- Sim, por baixo dos panos ela é uma função geradora, e o decorador contextmanager a envolve para expor os métodos __enter__ e __exit__. Você não interage com ela como um gerador comum, não itera sobre ela; o decorador cuida de avançar até o yield na entrada e de retomá-la na saída.
- Quando prefiro a classe em vez da função com yield?
- Quando o gerenciador tem bastante estado, métodos próprios ou precisa ser reutilizado como objeto em contextos diferentes. Para a maioria dos casos simples, a função com yield é mais curta e legível. Uma boa regra é começar pela função e só migrar para a classe se sentir falta de estrutura.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.