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

    Young professional woman working on a laptop in an office setting, concentrating on her task.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextlib no Python: gerencie recursos

    Aprenda contextlib no Python com contextmanager, ExitStack, suppress, closing, asynccontextmanager e cleanup seguro de recursos.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Vivid close-up of code on a computer screen showcasing programming details.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ast no Python: analise código-fonte

    Aprenda ast no Python para analisar e transformar código, criar visitors, preservar posições, usar literal_eval e evitar riscos de execução.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Rustic exposed brick wall featuring aged electrical sockets and metal conduit.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    socket no Python: redes TCP e UDP

    Aprenda socket no Python para clientes e servidores TCP, UDP, framing, timeouts, IPv6, concorrência, TLS e segurança de rede.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    multiprocessing no Python: vários núcleos

    Aprenda multiprocessing no Python com processos, pools, filas, pipes, memória compartilhada, cancelamento, segurança e shutdown correto.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Monochrome image showcasing concentric circles in a tunnel-like structure creating a sense of depth.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    concurrent.futures: threads e processos em paralelo

    Aprenda concurrent.futures no Python com threads, processos, Future, timeouts, cancelamento, backpressure e prevenção de deadlocks.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026
    Striking image of a red-bellied python showcasing its vibrant scales in dramatic lighting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    winsound no Python: sons no Windows

    Aprenda winsound no Python para tocar WAV, sons do sistema, beeps, loops e notificações assíncronas com segurança no Windows.

    Ler mais

    Tempo de leitura: 7 minutos
    26/08/2026