---
title: "Context Managers em Python: Dominando o with"
url: "https://python.dev.br/blog/context-managers-python-with/"
markdown_url: "https://python.dev.br/blog/context-managers-python-with.MD"
description: "Aprenda context managers em Python com exemplos práticos usando classes e contextlib. Domine o with, crie seus próprios gerenciadores e use async with."
date: "2026-03-27"
author: "Equipe python.dev.br"
---

# Context Managers em Python: Dominando o with

Aprenda context managers em Python com exemplos práticos usando classes e contextlib. Domine o with, crie seus próprios gerenciadores e use async with.


Esquecer de fechar um arquivo, não liberar uma conexão com o banco de dados, deixar um lock travado — esses são erros sutis que causam bugs difíceis de rastrear. Os **context managers** do Python resolvem isso com elegância, garantindo que recursos sejam sempre liberados, mesmo quando ocorrem exceções.

## O Que São Context Managers

Um context manager é qualquer objeto que implementa os métodos `__enter__` e `__exit__`. Quando usado com a instrução `with`, o Python garante que `__enter__` é chamado no início do bloco e `__exit__` é chamado ao final — sempre, mesmo se uma exceção ocorrer no meio.

O exemplo mais comum é a abertura de arquivos:

```python
# Sem context manager — risco de vazamento de recurso
f = open("dados.txt", "r")
try:
    conteudo = f.read()
finally:
    f.close()

# Com context manager — limpo e seguro
with open("dados.txt", "r") as f:
    conteudo = f.read()
# f.close() é chamado automaticamente aqui
```

A versão com `with` é mais curta, mais legível e mais segura. Para mais detalhes sobre manipulação de arquivos, veja nosso guia de [manipulação de arquivos em Python](/blog/manipulacao-de-arquivos-python/).

## Criando Context Managers com Classes

Para criar seu próprio context manager, implemente `__enter__` e `__exit__`:

```python
class Temporizador:
    """Mede o tempo de execução de um bloco de código."""

    def __enter__(self):
        import time
        self.inicio = time.perf_counter()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        self.duracao = time.perf_counter() - self.inicio
        print(f"Executado em {self.duracao:.4f}s")
        return False  # não suprime exceções

# Uso
with Temporizador() as t:
    soma = sum(range(1_000_000))

print(f"Duração capturada: {t.duracao:.4f}s")
```

O método `__exit__` recebe três argumentos sobre exceções:

- `exc_type` — a classe da exceção (ou `None`)
- `exc_val` — a instância da exceção (ou `None`)
- `exc_tb` — o traceback (ou `None`)

Se `__exit__` retornar `True`, a exceção é suprimida. Se retornar `False` (ou `None`), a exceção é propagada normalmente. Na maioria dos casos, você quer propagar exceções — retorne `False`. Para entender melhor o tratamento de exceções, confira nosso guia de [tratamento de erros em Python](/blog/tratamento-de-erros-python/).

## O Decorador @contextmanager

Criar uma classe inteira só para um context manager simples pode ser excessivo. O módulo `contextlib` oferece o [decorador](/blog/decoradores-python-guia-pratico/) `@contextmanager`, que permite criar context managers usando geradores:

```python
from contextlib import contextmanager

@contextmanager
def diretorio_temporario(caminho: str):
    """Muda para um diretório e volta ao original no final."""
    import os
    original = os.getcwd()
    try:
        os.makedirs(caminho, exist_ok=True)
        os.chdir(caminho)
        yield caminho
    finally:
        os.chdir(original)

# Uso
with diretorio_temporario("/tmp/meu_projeto") as dir:
    print(f"Trabalhando em: {dir}")
    # ... operações no diretório temporário
# Automaticamente volta ao diretório original
```

A estrutura é sempre a mesma:

1. **Antes do `yield`** — código de setup (equivalente ao `__enter__`)
2. **O `yield`** — o valor entregue ao `as` do `with`
3. **Depois do `yield`** (no `finally`) — código de cleanup (equivalente ao `__exit__`)

Esse padrão se conecta com [geradores e iteradores](/blog/geradores-iteradores-python/), já que `@contextmanager` usa um gerador por baixo dos panos.

## Context Managers para Banco de Dados

Um dos usos mais práticos é gerenciar conexões e transações de banco de dados:

```python
from contextlib import contextmanager
import sqlite3

@contextmanager
def conexao_db(caminho: str):
    """Gerencia conexão SQLite com commit/rollback automático."""
    conn = sqlite3.connect(caminho)
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

@contextmanager
def transacao(conn):
    """Gerencia uma transação individual."""
    cursor = conn.cursor()
    try:
        yield cursor
        conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        cursor.close()

# Uso
with conexao_db("minha_app.db") as conn:
    with transacao(conn) as cursor:
        cursor.execute("CREATE TABLE IF NOT EXISTS usuarios (id INTEGER PRIMARY KEY, nome TEXT)")
        cursor.execute("INSERT INTO usuarios (nome) VALUES (?)", ("Ana",))
```

Esse padrão é usado extensivamente por ORMs como SQLAlchemy e por drivers de [PostgreSQL](/blog/python-e-postgresql/) e [SQLite](/blog/python-e-banco-de-dados-sqlite/). Também é comum em conexões com [Redis](/blog/python-e-redis/) e outros serviços.

## Context Managers Aninhados e ExitStack

Quando você precisa gerenciar múltiplos recursos, pode aninhar `with` statements ou usar uma única linha:

```python
# Múltiplos context managers em uma linha
with open("entrada.txt") as entrada, open("saida.txt", "w") as saida:
    saida.write(entrada.read().upper())
```

Para um número dinâmico de context managers, use `ExitStack`:

```python
from contextlib import ExitStack

def processar_arquivos(caminhos: list[str]):
    with ExitStack() as stack:
        arquivos = [
            stack.enter_context(open(caminho))
            for caminho in caminhos
        ]
        for arquivo in arquivos:
            print(arquivo.readline())

# Todos os arquivos são fechados automaticamente ao sair do with
processar_arquivos(["config.txt", "dados.csv", "log.txt"])
```

O `ExitStack` é especialmente útil quando a quantidade de recursos é determinada em tempo de execução.

## Utilitários Úteis do contextlib

O módulo `contextlib` oferece vários context managers prontos:

### suppress — ignorar exceções específicas

```python
from contextlib import suppress
import os

# Em vez de try/except para ignorar FileNotFoundError
with suppress(FileNotFoundError):
    os.remove("arquivo_temporario.txt")
```

### redirect\_stdout — redirecionar saída

```python
from contextlib import redirect_stdout
from io import StringIO

buffer = StringIO()
with redirect_stdout(buffer):
    print("Essa saída vai para o buffer")

resultado = buffer.getvalue()
print(f"Capturado: {resultado}")
```

### closing — garantir chamada de close()

```python
from contextlib import closing
from urllib.request import urlopen

with closing(urlopen("https://python.dev.br")) as pagina:
    conteudo = pagina.read()
```

## Context Managers Assíncronos

Com o crescimento de [programação assíncrona em Python](/blog/python-async-await/), context managers assíncronos se tornaram essenciais. Eles usam `__aenter__` e `__aexit__` com `async with`:

```python
from contextlib import asynccontextmanager
import aiohttp

@asynccontextmanager
async def sessao_http():
    """Gerencia uma sessão HTTP assíncrona."""
    session = aiohttp.ClientSession()
    try:
        yield session
    finally:
        await session.close()

# Uso
async def buscar_dados():
    async with sessao_http() as session:
        async with session.get("https://api.exemplo.com/dados") as resp:
            return await resp.json()
```

O `@asynccontextmanager` funciona exatamente como `@contextmanager`, mas com `async def` e `yield`.

## Padrões Comuns em Bibliotecas Populares

Context managers aparecem em praticamente toda biblioteca Python de qualidade:

```python
# SQLAlchemy — sessões de banco de dados
from sqlalchemy.orm import Session

with Session(engine) as session:
    usuario = session.get(Usuario, 1)
    session.commit()

# requests — sessões HTTP com conexão persistente
import requests

with requests.Session() as s:
    s.headers.update({"Authorization": "Bearer token"})
    resposta = s.get("https://api.exemplo.com/dados")

# tempfile — arquivos temporários
import tempfile

with tempfile.NamedTemporaryFile(mode="w", suffix=".csv") as tmp:
    tmp.write("nome,idade\nAna,28\n")
    tmp.flush()
    # arquivo existe enquanto estiver no bloco with

# threading — locks
import threading

lock = threading.Lock()
with lock:
    # operação thread-safe
    contador += 1
```

## Tratamento de Erros em Context Managers

Um context manager bem escrito deve lidar com exceções de forma previsível:

```python
from contextlib import contextmanager
import logging

logger = logging.getLogger(__name__)

@contextmanager
def operacao_segura(nome: str):
    """Context manager com logging de erros."""
    logger.info(f"Iniciando operação: {nome}")
    try:
        yield
    except Exception as e:
        logger.error(f"Erro na operação {nome}: {e}")
        raise  # sempre re-levanta a exceção
    else:
        logger.info(f"Operação {nome} concluída com sucesso")
    finally:
        logger.debug(f"Cleanup da operação: {nome}")

with operacao_segura("importar_dados"):
    # seu código aqui
    dados = processar()
```

Use [logging](/blog/logging-em-python/) dentro dos seus context managers para rastrear o ciclo de vida dos recursos. E lembre-se: a menos que tenha um motivo muito específico, sempre re-levante exceções no `__exit__`.

## Criando Context Managers com Dataclasses

Context managers combinam bem com [dataclasses](/blog/dataclasses-python-guia-completo/) para criar gerenciadores de recurso configuráveis:

```python
from dataclasses import dataclass, field
from contextlib import contextmanager
from datetime import datetime

@dataclass
class ConfiguracaoCache:
    host: str = "localhost"
    porta: int = 6379
    timeout: int = 30
    _conexao: object = field(init=False, repr=False, default=None)

    @contextmanager
    def conectar(self):
        """Gerencia conexão com o cache."""
        import redis
        self._conexao = redis.Redis(
            host=self.host, port=self.porta,
            socket_timeout=self.timeout
        )
        try:
            yield self._conexao
        finally:
            self._conexao.close()
            self._conexao = None

config = ConfiguracaoCache(host="cache.exemplo.com")
with config.conectar() as cache:
    cache.set("chave", "valor")
```

## Considerações Finais

Context managers são fundamentais para escrever código Python robusto e profissional. Eles garantem que recursos sejam sempre liberados, tornam o código mais legível e seguem o princípio de que o cleanup não deve depender da memória do programador.

Use `with` sempre que trabalhar com recursos que precisam ser liberados — arquivos, conexões, locks, sessões. Crie seus próprios context managers com `@contextmanager` para operações simples ou com classes quando precisar de mais controle. E para mais dicas de código Python limpo e profissional, confira nosso guia de [boas práticas Python em 2026](/blog/boas-praticas-python-2026/).

Curiosidade: outras linguagens resolvem gerenciamento de recursos de formas diferentes — <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go usa `defer`</a> para garantir cleanup, enquanto <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust usa RAII e ownership</a> para liberar recursos automaticamente em tempo de compilação.
