A instrução que se limpa sozinha
Todo recurso que você abre num programa — um arquivo, uma conexão de rede, um handle de banco de dados, um lock — precisa ser fechado quando você acaba. Esqueça e você vaza memória, segura locks que bloqueiam outros processos ou corrompe arquivos em crash. A instrução with do Python cuida disso para você.
O padrão com que todo mundo começa é ler um arquivo:
with open("notes.txt") as f:
contents = f.read()
print(contents)
Duas coisas acontecem automaticamente. Na entrada, open() te dá um objeto de arquivo amarrado a f. Na saída — seja o bloco terminando normalmente, retornando cedo ou lançando — o Python chama f.close() para você.
É isso. Esse é o ponto todo do with.
O que o "with" substitui
Antes de context managers, o código equivalente seguro era um try/finally:
f = open("notes.txt")
try:
contents = f.read()
print(contents)
finally:
f.close()
Cinco linhas de cerimônia para "leia um arquivo e feche quando terminar". Multiplique isso por cada open num programa maior e você começa a ver o apelo. with é mais curto, mais difícil de errar e impossível de esquecer a limpeza.
Abrindo vários recursos
Você pode amarrar vários context managers em um with:
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read().upper())
Os dois arquivos abrem na entrada, os dois fecham na saída. Se o primeiro open dá certo e o segundo lança, o Python ainda fecha o primeiro — a maquinaria lida com setup parcial corretamente.
Para listas mais longas de recursos, a forma com parênteses (Python 3.10+) é mais clara:
with (
open("a.txt") as a,
open("b.txt") as b,
open("c.txt") as c,
):
...
O que um context manager de fato é
Qualquer objeto que define __enter__ e __exit__ é um context manager. O protocolo é bobo:
__enter__(self)roda quando o blocowithcomeça. Seu valor de retorno é o queas nomeamarra.__exit__(self, exc_type, exc_value, traceback)roda quando o bloco termina, seja como for. Se uma exceção causou a saída, a info da exceção é passada para que o context manager possa inspecionar ou suprimir.
Aqui vai um mínimo que cronometra o bloco que envolve:
with Timer(): cria o objeto, chama o __enter__, roda o corpo, chama o __exit__. Sem arquivo, sem lock — só um pequeno wrapper em torno de "faça algo, meça quanto demorou".
O atalho contextlib.contextmanager
Definir uma classe para cada context manager é mais pesado do que precisa. contextlib.contextmanager transforma uma função geradora num context manager — um yield separa o "antes" do "depois":
Tudo antes do yield é o comportamento __enter__. Tudo depois é o __exit__. O try/finally faz a limpeza rodar mesmo se o corpo lança.
A maioria dos context managers customizados que você vai escrever encaixam nesse formato. Recorra à forma com decorador primeiro; desça para uma classe só quando precisa de algo que a forma com gerador não expressa.
Mudando algo temporariamente
Um formato comum: configure algo, use, restaure. Context managers expressam isso de forma limpa:
Qualquer padrão "configure, depois restaure" — env vars, verbosidade de logging, feature flags, fixtures de teste — encaixa num context manager naturalmente. Quem chama não precisa lembrar de restaurar nada.
Suprimindo exceções
O método __exit__ pode retornar True para dizer ao Python "eu tratei a exceção; engula". Isso é raro e geralmente cheiro ruim, mas é como contextlib.suppress funciona:
suppress(FileNotFoundError) transforma o FileNotFoundError em no-op. Use para operações genuinamente opcionais — "tente isto, não ligo se não funcionar". Não use para silenciar exceções sobre as quais você não pensou.
Outros context managers que você vai encontrar
Context managers aparecem por toda a biblioteca padrão quando você começa a olhar:
import threading
from pathlib import Path
# Locks — guarantee release even if the critical section raises.
lock = threading.Lock()
with lock:
...
# tempfile — delete the temp file when you're done.
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmp:
path = Path(tmp) / "scratch.txt"
path.write_text("hello")
# Database connections — close the connection (or end the transaction).
import sqlite3
with sqlite3.connect(":memory:") as conn:
conn.execute("CREATE TABLE t (x INTEGER)")
Bibliotecas de terceiros seguem as mesmas convenções. Quando você vê with alguma_coisa as x:, quase sempre significa "use x pela duração deste bloco, e limpe depois".
Quando não usar with
- Quando você não tem setup e teardown de fato. Envolver código arbitrário num context manager sem razão adiciona ruído.
- Quando você precisa do recurso em muitos blocos não relacionados. Manter um
withaberto pela vida inteira de um script longo pode esconder qual é o escopo real da limpeza. Considere uma classe que possua o recurso. - Quando um decorador encaixa melhor. Alguns padrões repetidos (retry, log, timer) leem mais natural como
@decoratornuma função do que comowith ...:dentro. Escolha o que lê melhor no ponto de chamada.
Na maior parte do tempo, with é certo. As exceções raras são fáceis de identificar quando você está procurando.
Próxima: trabalhando com arquivos de verdade
Você agora sabe a mecânica por trás de with open(...) as f: — que é o contexto em que vai usar em noventa por cento das vezes. O próximo capítulo coloca isso para trabalhar lendo, escrevendo e navegando arquivos no disco.
Perguntas frequentes
O que with open faz em Python?
with open faz em Python?with open(path) as f: abre o arquivo e amarra a f pela duração do bloco. Quando o bloco termina — normalmente ou por exceção — o Python fecha o arquivo automaticamente. Você não precisa de f.close(); a instrução with garante.
Por que usar with em vez de open() puro?
with em vez de open() puro?Porque with fecha o arquivo mesmo quando uma exceção dispara no meio do bloco. open() puro deixa você responsável por lembrar de close() em todo caminho de código, incluindo caminhos de erro. with é mais seguro e mais curto.
Como abro vários arquivos com uma instrução with?
with?Separe os context managers por vírgula: with open('a.txt') as a, open('b.txt') as b:. Os dois arquivos são abertos na entrada e fechados na saída, em ordem inversa. Isso substitui instruções with aninhadas quando você precisa de vários recursos ao mesmo tempo.