weakref: evite reter objetos em caches

Publicado em: 27/08/2026
Tempo de leitura: 8 minutos
A developer typing code on a laptop with a Python book beside in an office.

O módulo weakref permite criar referências que apontam para um objeto sem impedir que ele seja coletado pelo garbage collector. Em uma referência normal, o objeto permanece vivo enquanto existir ao menos um vínculo forte. Em uma referência fraca, o vínculo pode ser consultado enquanto o objeto existe, mas não aumenta sua contagem de referências nem prolonga artificialmente seu ciclo de vida.

Esse recurso é útil em caches, registros de objetos, mapas de metadados, observers, árvores de componentes e estruturas que não devem ser proprietárias dos elementos armazenados. Referências fracas também ajudam a evitar ciclos de vida acidentais, mas não substituem uma arquitetura clara de ownership. O desenvolvedor precisa entender quando o objeto pode desaparecer e como o código reage a essa situação.

Referência forte e referência fraca

Uma variável comum mantém uma referência forte.

objeto = MinhaClasse()
alias = objeto

Enquanto objeto ou alias existir, a instância permanece acessível. Com weakref.ref(), a referência pode deixar de apontar para uma instância válida quando todas as referências fortes forem removidas.

import weakref

objeto = MinhaClasse()
referencia = weakref.ref(objeto)
print(referencia() is objeto)

del objeto
print(referencia())

A chamada referencia() devolve o objeto vivo ou None depois da coleta.

Nem todo objeto aceita weakref

Instâncias de classes definidas pelo usuário normalmente aceitam referências fracas. Muitos tipos embutidos, como list e dict, não aceitam diretamente, embora subclasses possam aceitar em determinadas condições.

import weakref

try:
    weakref.ref([])
except TypeError as erro:
    print(erro)

Antes de construir uma API genérica, trate TypeError ou documente que os objetos precisam oferecer suporte a weak references.

Classes com __slots__

Ao usar __slots__, inclua "__weakref__" para permitir referências fracas.

class Usuario:
    __slots__ = ("nome", "__weakref__")

    def __init__(self, nome):
        self.nome = nome

Sem esse slot especial, weakref.ref(instancia) gera TypeError. Isso é uma decisão de layout da classe e deve ser tomada antes da distribuição da API.

Evite a corrida entre teste e uso

Não teste a referência e depois a chame novamente, porque o objeto pode desaparecer entre as operações em código concorrente.

objeto = referencia()
if objeto is not None:
    objeto.executar()

Guardar o resultado em uma variável local cria uma referência forte temporária durante o uso.

Callbacks de finalização da referência

weakref.ref() pode receber um callback chamado quando o objeto referenciado está prestes a desaparecer.

import weakref


def removido(referencia):
    print("objeto coletado")

objeto = MinhaClasse()
referencia = weakref.ref(objeto, removido)

O callback recebe a própria referência fraca, não o objeto original. Nesse momento, recuperar o objeto normalmente já não é possível.

Não capture o objeto no callback

Um erro comum é criar uma closure que mantém uma referência forte ao próprio objeto.

def criar_callback(objeto):
    def callback(referencia):
        print(objeto)
    return callback

Esse padrão impede a coleta e anula o propósito da weak reference. Capture apenas identificadores, strings ou metadados independentes.

weakref.proxy

weakref.proxy() cria um proxy que encaminha operações ao objeto sem exigir a chamada ().

import weakref

objeto = MinhaClasse()
proxy = weakref.proxy(objeto)
proxy.executar()

Se o objeto já tiver sido coletado, o proxy gera ReferenceError. Use quando a sintaxe transparente realmente melhorar a API; caso contrário, ref() torna a possibilidade de ausência mais explícita.

Proxy para objetos chamáveis

Funções e objetos com __call__ podem gerar CallableProxyType. O proxy continua sem possuir o alvo.

Não armazene um proxy em um local que exige disponibilidade garantida. A chamada precisa aceitar ReferenceError como parte normal do ciclo de vida.

WeakValueDictionary

WeakValueDictionary mantém chaves fortes e valores fracos. Quando um valor não possui mais referências fortes, a entrada desaparece automaticamente.

import weakref

cache = weakref.WeakValueDictionary()
objeto = MinhaClasse()
cache["principal"] = objeto
print("principal" in cache)

del objeto
print("principal" in cache)

É útil para interning, factories e caches que não devem prolongar a vida dos resultados.

Cache fraco não garante retenção

Uma entrada pode sumir logo após a criação se nenhum caller mantiver referência forte.

cache[chave] = construir()

Se construir() devolver um objeto sem outro proprietário, ele pode desaparecer imediatamente. O caller que precisa do valor deve armazená-lo em uma variável forte.

WeakKeyDictionary

WeakKeyDictionary mantém chaves fracas e valores fortes. Quando a chave desaparece, a associação é removida.

import weakref

metadados = weakref.WeakKeyDictionary()
usuario = Usuario("Ana")
metadados[usuario] = {"visitas": 1}

É uma alternativa a adicionar atributos em objetos de terceiros e pode armazenar dados auxiliares associados à vida da instância.

Igualdade e identidade nas chaves

Objetos diferentes que são considerados iguais podem interagir de forma inesperada em mapas fracos. O dicionário segue regras de hash e igualdade, enquanto a remoção depende da vida do objeto usado como chave.

Prefira chaves cuja identidade e igualdade sejam estáveis. Não altere campos usados em __hash__ depois da inserção.

WeakSet

WeakSet armazena objetos fracamente, removendo membros coletados.

import weakref

observadores = weakref.WeakSet()
observadores.add(listener)

for observador in list(observadores):
    observador.atualizar()

O snapshot com list() pode ser útil quando callbacks alteram o conjunto durante a iteração.

Observers sem vazamento

Um sistema de eventos que mantém listeners em uma lista normal pode impedir que telas, componentes ou controllers sejam coletados. Um WeakSet reduz esse risco quando os listeners são objetos.

Ainda é necessário definir o que acontece quando nenhum listener existe e como funções, métodos e closures são registrados.

WeakMethod

Um método bound temporário não pode ser tratado da mesma forma que uma instância comum, porque acessar objeto.metodo cria um novo objeto de método. WeakMethod guarda uma referência fraca recuperável ao par instância e função.

import weakref

referencia = weakref.WeakMethod(objeto.processar)
metodo = referencia()
if metodo is not None:
    metodo()

Esse padrão é especialmente útil em registradores de callbacks orientados a objetos.

finalize

weakref.finalize() registra uma função para ser chamada quando um objeto é coletado.

import weakref

recurso = Recurso()
finalizador = weakref.finalize(recurso, fechar_handle, recurso.handle)

O finalizador permanece vivo até executar ou ser cancelado. Ele costuma ser mais conveniente e robusto que callbacks diretos de ref().

Não use finalização como mecanismo principal

A liberação determinística deve usar with, close() ou uma API explícita. A coleta pode ocorrer tarde, em ordem inesperada ou durante o encerramento do interpretador.

Use finalize como rede de segurança, não como transação, commit, flush crítico ou fechamento que precisa ocorrer em prazo definido.

detach e alive

Um finalizador possui a propriedade alive. detach() remove e devolve as informações registradas sem executar a função. Uma chamada direta ao finalizador executa apenas uma vez.

if finalizador.alive:
    finalizador()

Isso permite cleanup explícito enquanto preserva a proteção contra duplicidade.

Ordens de finalização

Durante o shutdown, finalizadores vivos podem ser chamados em ordem reversa de criação, conforme as regras da implementação. Não construa dependências complexas entre eles.

Um finalizador não deve depender de módulos globais que talvez já estejam parcialmente desmontados. Passe funções e valores necessários diretamente.

Ciclos de referência

Referências fracas podem quebrar vínculos não proprietários em grafos de objetos. Por exemplo, um filho pode possuir uma referência fraca ao pai quando o pai já possui fortemente o filho.

O garbage collector moderno consegue resolver muitos ciclos, mas weak references continuam úteis para expressar ownership e evitar retenção por registradores externos.

Garbage collection não é imediata em todos os runtimes

Em CPython, a contagem de referências costuma liberar muitos objetos rapidamente. Outras implementações podem coletar em momentos diferentes. Mesmo em CPython, ciclos dependem do coletor cíclico.

Não escreva testes que exigem que o callback ocorra exatamente após del. Quando necessário em testes controlados, gc.collect() pode solicitar uma coleta, mas não deve virar requisito da aplicação.

Threads e segurança

Estruturas fracas não transformam operações compostas em atômicas. Um objeto pode desaparecer entre observações, e dicionários podem mudar durante iteração.

Use locks quando várias threads atualizam o mesmo registrador. Sempre obtenha o alvo uma única vez e mantenha a referência local durante o uso.

Asyncio e tasks

O event loop pode manter apenas referências específicas a tasks em certos contextos. A documentação de asyncio recomenda que a aplicação mantenha referências fortes para tasks de background e remova-as ao concluir.

Não presuma que uma estrutura fraca é adequada para gerenciar tarefas cuja execução deve ser garantida.

Desempenho

Weak references têm overhead de objetos auxiliares, callbacks e manutenção de estruturas. Elas também tornam o lifecycle menos previsível para quem lê o código.

Use-as quando resolvem um problema real de ownership ou cache. Uma lista normal com remoção explícita pode ser mais simples e rápida.

Debugging

weakref.getweakrefcount(objeto) informa quantas referências fracas e proxies apontam para o objeto. getweakrefs() devolve a lista correspondente.

print(weakref.getweakrefcount(objeto))
print(weakref.getweakrefs(objeto))

Essas funções ajudam em diagnóstico, mas o estado pode mudar imediatamente em código concorrente.

Testes recomendados

Teste o objeto vivo, coleta após remoção da última referência forte, callback executado uma vez, finalizador explícito, cancelamento, objetos sem suporte a weakref, classes com slots, concorrência e ausência inesperada no cache.

Evite depender da ordem global de coleta entre vários objetos.

Erros comuns

Os erros mais frequentes são usar weak reference quando o cache precisa reter valores, capturar o alvo no callback, reutilizar um proxy depois da coleta, esquecer __weakref__ em slots, esperar coleta imediata, usar finalizador para commit crítico e não manter referência forte para callbacks ou tasks que precisam continuar vivos.

Conclusão

weakref permite representar relações não proprietárias e construir caches que cedem espaço automaticamente quando os objetos deixam de ser usados. Use ref para acesso explícito, proxies para sintaxe transparente, dicionários e sets fracos para registros e finalize como proteção adicional de cleanup.

Mantenha o ownership principal explícito, aceite que o alvo pode desaparecer e prefira context managers para liberação determinística. Consulte a documentação oficial de weakref e o artigo sobre contextlib no Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Rack de servidores representando balanceamento de conexões com SO_REUSEPORT_LB no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: distribua conexões entre workers

    Aprenda SO_REUSEPORT_LB no Python para distribuir conexões entre múltiplos workers com segurança, testes e portabilidade.

    Ler mais

    Tempo de leitura: 6 minutos
    11/10/2026
    Código Python assíncrono em notebook para inspect.markcoroutinefunction
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: identifique wrappers async

    Aprenda inspect.markcoroutinefunction no Python para identificar wrappers assíncronos, integrar frameworks e evitar detecção incorreta de corrotinas.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Código Python para percorrer pastas e arquivos com Path.walk
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: percorra diretórios com segurança

    Aprenda Path.walk no Python para percorrer diretórios, filtrar arquivos, tratar erros e controlar a travessia com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    10/10/2026
    Depuração de processo Python em terminal com código
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depure processos Python em execução

    Aprenda a anexar o pdb a um processo Python em execução, inspecionar pilhas e diagnosticar travamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python e representação de frações numéricas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: converta números em frações

    Aprenda fractions.from_number no Python para converter números em frações exatas, controlar precisão e evitar arredondamentos inesperados.

    Ler mais

    Tempo de leitura: 5 minutos
    09/10/2026
    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026