Módulo 6 - Context managers e o with

O protocolo __enter__ e __exit__

10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Entender que with chama __enter__ ao entrar e __exit__ ao sair.
  • Escrever uma classe context manager com os dois métodos.
  • Saber que o retorno de __enter__ é o que vai para o as.
  • Reconhecer os parâmetros de __exit__ e seu papel.

Os dois métodos que definem um context manager

O with não é mágico: ele apenas chama dois métodos especiais do objeto que você passa. Ao entrar no bloco, o Python chama o __enter__ do objeto; o valor que esse método devolve é atribuído à variável depois do as. Ao sair do bloco, por qualquer motivo, o Python chama o __exit__, onde vai a limpeza. Qualquer objeto que tenha esses dois métodos pode ser usado com with. Criar um context manager é, no fundo, escrever essas duas funções.

class Conexao:
    def __init__(self, host):
        self.host = host

    def __enter__(self):
        print(f"conectando a {self.host}")
        self.ativa = True
        return self          # vai para a variavel do as

    def __exit__(self, exc_tipo, exc_valor, traceback):
        print(f"desconectando de {self.host}")
        self.ativa = False
        return False         # nao suprime excecoes

with Conexao("servidor") as conn:
    print(f"usando conexao, ativa = {conn.ativa}")
# conectando a servidor
# usando conexao, ativa = True
# desconectando de servidor

Uma classe com __enter__ e __exit__ vira um context manager utilizável com with.

Acompanhe a ordem. O with Conexao("servidor") as conn cria o objeto e chama seu __enter__, que imprime a conexão e devolve self; por isso conn passa a ser a própria conexão. O bloco roda. Na saída, o Python chama __exit__, que imprime a desconexão. Repare que __enter__ costuma devolver self quando o objeto útil é ele mesmo, mas poderia devolver qualquer coisa, um arquivo, um cursor, um valor. É esse retorno que aparece depois do as.

Os parâmetros do __exit__

O __enter__ é simples; o __exit__ tem um detalhe importante. Ele recebe três parâmetros que descrevem a exceção que porventura ocorreu dentro do bloco: o tipo da exceção, o valor (a instância do erro) e o traceback. Se o bloco terminou sem erro, os três chegam como None. Esse desenho permite que o __exit__ saiba se a saída foi tranquila ou por falha, e reaja de formas diferentes, por exemplo desfazendo uma transação em caso de erro e confirmando em caso de sucesso.

Parâmetro de __exit__O que contémSe não houve erro
exc_tipoA classe da exceção (ex.: ValueError)None
exc_valorA instância da exceçãoNone
tracebackO objeto de rastreamentoNone

Os três parâmetros de __exit__ descrevem a exceção; None significa saída sem erro.

class Transacao:
    def __enter__(self):
        print("iniciando transacao")
        return self

    def __exit__(self, exc_tipo, exc_valor, traceback):
        if exc_tipo is None:
            print("commit: tudo certo")
        else:
            print(f"rollback: erro {exc_tipo.__name__}")
        return False

with Transacao():
    print("gravando dados")
    raise ValueError("dado invalido")
# iniciando transacao
# gravando dados
# rollback: erro ValueError
# ... e a excecao continua subindo

O __exit__ inspeciona exc_tipo para decidir entre confirmar e desfazer a operação.

Montando o seu, do começo ao fim

Junte as peças e você tem uma receita: no __init__ guarde a configuração; no __enter__ adquira o recurso e devolva o que o usuário vai manipular; no __exit__ libere o recurso e decida o que fazer com uma eventual exceção. Essa estrutura funciona para arquivos, conexões, bloqueios, medições de tempo, mudanças temporárias de configuração e muito mais. Sempre que você pensar em algo que tem começo e fim garantidos, um context manager de classe é a ferramenta.

Vale separar bem o __init__ do __enter__. O __init__ apenas prepara os dados; a aquisição de verdade acontece no __enter__, quando o with começa. Essa separação permite, por exemplo, criar o objeto uma vez e usá-lo em vários blocos with. Nas próximas aulas você vai ver que, para casos simples, existe um atalho que evita escrever a classe inteira. Mas entender o protocolo de classe é a base: o atalho é apenas uma forma mais curta de produzir os mesmos __enter__ e __exit__.

Teste rápido

No protocolo de context manager, o que vai para a variável depois do as em with obj as x?

Perguntas frequentes

O __enter__ precisa devolver self?
Não precisa. É comum devolver self quando o próprio objeto é o recurso útil, mas o __enter__ pode devolver qualquer coisa: um arquivo aberto, um cursor de banco, um valor calculado. O que ele devolve é o que aparece depois do as. Se você não precisa de nenhum valor no bloco, pode nem usar o as.
O que são os três parâmetros do __exit__?
São informações sobre a exceção que porventura ocorreu no bloco: o tipo da exceção, a instância dela e o traceback. Se o bloco terminou sem erro, os três chegam como None. Com esses dados, o __exit__ decide como agir, por exemplo confirmar a operação quando não houve erro e desfazê-la quando houve.
Quando o __exit__ é chamado?
Sempre que o fluxo sai do bloco with, aconteça o que acontecer: saída normal ao chegar ao fim do bloco, um return, um break, ou uma exceção. Essa garantia é justamente o que torna o with confiável para limpeza. Mesmo que uma exceção esteja subindo, o __exit__ roda antes de o erro continuar.
Devo adquirir o recurso no __init__ ou no __enter__?
No __enter__. O __init__ deve só guardar a configuração; a aquisição de fato (abrir o arquivo, conectar, travar o lock) pertence ao __enter__, que roda quando o with começa. Assim o objeto pode ser criado antes e o recurso só é adquirido no momento certo, e você pode até reutilizar o objeto em vários blocos.
Preciso criar uma classe para todo context manager?
Não. A classe com __enter__ e __exit__ é a forma mais completa e é útil quando há estado a guardar. Para casos simples, o contextlib.contextmanager permite criar um gerenciador a partir de uma função com yield, que você vê na próxima aula. Os dois produzem o mesmo comportamento; muda só a quantidade de código.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.