weakref no Python: referências fracas

Publicado em: 28/07/2026
Tempo de leitura: 9 minutos
Módulo de memória representando referências fracas e caches no Python

O gerenciamento automático de memória é uma das maiores conveniências do Python, mas referências comuns podem manter objetos vivos por mais tempo do que o necessário. Um cache, um registro de observadores ou um dicionário de metadados pode conservar objetos grandes mesmo quando o restante da aplicação já deixou de usá-los. O módulo weakref no Python oferece referências fracas: ligações que permitem acessar um objeto enquanto ele existe, sem impedir que o coletor de lixo recupere sua memória.

Neste guia, você aprenderá a diferença entre referências fortes e fracas, como usar weakref.ref(), WeakValueDictionary, WeakKeyDictionary, WeakSet, WeakMethod, proxies e finalize(). O tema complementa os artigos sobre detecção de vazamentos de memória, descriptors em Python, estruturas do módulo collections, análise com cProfile e otimização de scripts Python.

Referências fortes e ciclo de vida

Uma variável comum mantém uma referência forte para o objeto. Enquanto pelo menos uma referência forte existir, o objeto continua vivo. Isso vale para variáveis locais, atributos, elementos de listas e valores de dicionários.

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

imagem = Imagem("capa.png")
cache = {"capa": imagem}
del imagem

print(cache["capa"].nome)

Mesmo após apagar a variável imagem, o dicionário ainda mantém o objeto vivo. Esse comportamento é correto para a maioria dos casos, mas pode ser indesejado em caches e índices auxiliares. Uma referência fraca não aumenta a contagem de referências fortes. Quando só restam referências fracas, o objeto pode ser destruído.

Criando uma referência com weakref.ref

A função weakref.ref() devolve um objeto chamável. Ao chamá-lo, você recebe o referente se ele ainda estiver vivo ou None se já tiver sido coletado.

import weakref

class Documento:
    pass

documento = Documento()
referencia = weakref.ref(documento)

print(referencia() is documento)
del documento
print(referencia())

A documentação oficial de weakref chama de referente o objeto apontado pela referência fraca. O detalhe central é que a própria referência fraca não prolonga a vida desse referente.

A forma segura de desreferenciar

Não faça uma verificação separada e depois chame novamente a referência. Em aplicações concorrentes, o objeto pode desaparecer entre as duas operações. Recupere o valor uma vez e mantenha a referência forte local durante o uso.

objeto = referencia()
if objeto is None:
    print("O objeto já foi removido")
else:
    objeto.processar()

Ao armazenar o resultado em objeto, o programa cria temporariamente uma referência forte. Isso evita que o referente desapareça no meio da operação.

Callbacks quando o objeto é removido

weakref.ref() aceita um callback executado quando o referente está prestes a ser finalizado. O callback recebe a própria referência fraca, não o objeto que já está indisponível.

import weakref

class Sessao:
    pass

def removida(referencia):
    print("Sessão removida do índice")

sessao = Sessao()
referencia = weakref.ref(sessao, removida)
del sessao

Evite capturar o objeto dentro do callback por closure, argumento ou método vinculado. Isso criaria uma referência forte indireta e impediria justamente a coleta que o callback deveria observar. Exceções lançadas durante o callback são registradas na saída de erro, mas não podem ser propagadas para o código que removeu a última referência.

Cache automático com WeakValueDictionary

WeakValueDictionary mantém chaves normais e valores fracos. Quando um valor deixa de ter referências fortes em outras partes da aplicação, a entrada é removida automaticamente.

from weakref import WeakValueDictionary

class Perfil:
    def __init__(self, identificador):
        self.identificador = identificador

_cache = WeakValueDictionary()

def carregar_perfil(identificador):
    perfil = _cache.get(identificador)
    if perfil is None:
        perfil = Perfil(identificador)
        _cache[identificador] = perfil
    return perfil

perfil = carregar_perfil(42)
print(list(_cache))
del perfil
print(list(_cache))

Esse padrão é útil para objetos caros que podem ser recriados: imagens decodificadas, modelos temporários, wrappers de recursos e representações intermediárias. O cache não garante que o valor estará disponível na próxima consulta. Ele é uma otimização, não uma fonte persistente de dados.

Quando um cache fraco não é suficiente

Um cache fraco pode esvaziar rapidamente se o chamador não mantiver referências fortes. Também não define limite de tamanho, política LRU, tempo de expiração nem sincronização. Para resultados pequenos e reutilizados por chave, functools.lru_cache() pode ser mais adequado. Para dados persistentes, use banco de dados, arquivo ou armazenamento distribuído.

Escolha WeakValueDictionary quando a regra for: “reutilize o objeto enquanto outra parte do programa precisar dele, mas não o mantenha vivo somente por causa do cache”.

Metadados externos com WeakKeyDictionary

WeakKeyDictionary mantém as chaves fracamente. Ele permite associar informações a objetos de terceiros sem adicionar atributos nem impedir sua coleta.

from weakref import WeakKeyDictionary

class Conexao:
    pass

metricas = WeakKeyDictionary()
conexao = Conexao()
metricas[conexao] = {"consultas": 3}

print(metricas[conexao])
del conexao
print(len(metricas))

Esse recurso é útil em instrumentação, descriptors, frameworks, adaptadores e sistemas de plugins. Quando a instância principal desaparece, seus metadados auxiliares também são eliminados.

Cuidado com igualdade e identidade nas chaves

Em WeakKeyDictionary, inserir uma chave diferente, mas considerada igual por __eq__(), pode substituir o valor sem trocar o objeto usado internamente como chave. Se a chave original for coletada, a entrada pode desaparecer mesmo que o segundo objeto ainda exista.

Quando objetos distintos podem ser iguais, remova explicitamente a entrada antiga antes de inserir a nova ou use uma chave estável, como um identificador imutável. Teste esse comportamento em classes que personalizam igualdade e hash.

Conjuntos de observadores com WeakSet

WeakSet implementa a interface de conjunto, mas não mantém os elementos vivos. Ele funciona bem para registros de ouvintes e objetos ativos.

from weakref import WeakSet

class Observador:
    def atualizar(self, evento):
        print("Evento:", evento)

observadores = WeakSet()
observador = Observador()
observadores.add(observador)

for item in list(observadores):
    item.atualizar("dados alterados")

del observador
print(len(observadores))

Converta para uma lista antes de iterar quando callbacks puderem alterar o conjunto ou quando a estabilidade da iteração for importante. Um registro fraco evita que o publicador se torne responsável pela vida de todos os assinantes.

Métodos vinculados e WeakMethod

Um método vinculado, como objeto.executar, é um objeto temporário criado durante o acesso. Uma referência fraca comum para esse método pode expirar imediatamente. WeakMethod guarda separadamente a função e a instância e consegue reconstruir o método enquanto ambas existirem.

from weakref import WeakMethod

class Tela:
    def redesenhar(self):
        print("Redesenhando")

tela = Tela()
callback = WeakMethod(tela.redesenhar)

metodo = callback()
if metodo is not None:
    metodo()

del tela
print(callback())

Esse padrão é valioso em sistemas de eventos, interfaces gráficas e barramentos de mensagens que não devem manter consumidores vivos indefinidamente.

Proxies fracos

weakref.proxy() cria um proxy que encaminha operações ao referente. Ele é conveniente quando você quer usar o objeto quase normalmente, sem chamar uma referência explicitamente.

import weakref

class Configuracao:
    ambiente = "produção"

configuracao = Configuracao()
proxy = weakref.proxy(configuracao)
print(proxy.ambiente)

del configuracao

try:
    print(proxy.ambiente)
except ReferenceError:
    print("Configuração indisponível")

Depois da coleta, acessar o proxy gera ReferenceError. Proxies também não são hashable, mesmo quando o referente é. Para código de biblioteca, uma referência explícita costuma deixar o estado ausente mais visível.

Limpeza com weakref.finalize

weakref.finalize() registra uma função de limpeza que será chamada no máximo uma vez quando o objeto for coletado. O objeto finalizador permanece vivo automaticamente, o que o torna mais simples que um callback de referência crua.

import tempfile
import shutil
import weakref

class PastaTemporaria:
    def __init__(self):
        self.caminho = tempfile.mkdtemp()
        self._finalizador = weakref.finalize(
            self,
            shutil.rmtree,
            self.caminho,
            ignore_errors=True,
        )

    def remover(self):
        self._finalizador()

    @property
    def removida(self):
        return not self._finalizador.alive

O finalizador também pode ser chamado explicitamente, e chamadas posteriores não repetem a limpeza. Por padrão, finalizadores ainda vivos são executados na saída normal do interpretador em ordem inversa à criação.

Não capture o próprio objeto no finalizador

A função, os argumentos e os argumentos nomeados de finalize() não podem manter referência direta ou indireta ao objeto monitorado. Um erro comum é passar um método vinculado da própria instância.

# Evite:
# weakref.finalize(self, self.fechar)

# Prefira uma função externa e apenas os dados necessários:
weakref.finalize(self, fechar_recurso, identificador)

Se o callback possuir self, a instância mantém o finalizador, o finalizador mantém o método e o método mantém a instância. O objeto pode nunca se tornar elegível para coleta.

Tipos que aceitam referências fracas

Instâncias de classes definidas em Python normalmente aceitam referências fracas. Funções Python, métodos, conjuntos, geradores, sockets, arrays e vários outros tipos também são compatíveis. Porém, listas e dicionários embutidos não aceitam diretamente.

import weakref

class MinhaLista(list):
    pass

valores = MinhaLista([1, 2, 3])
referencia = weakref.ref(valores)
print(referencia())

Subclasses de list e dict podem ganhar suporte, mas alguns tipos, como tuple e int, continuam incompatíveis mesmo quando subclassificados.

weakref e __slots__

Ao declarar __slots__, a classe perde o suporte automático a referências fracas, a menos que inclua "__weakref__".

import weakref

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

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

usuario = Usuario("Ana")
referencia = weakref.ref(usuario)
print(referencia().nome)

Esse detalhe é especialmente importante em classes com muitas instâncias, onde __slots__ é usado para reduzir memória e weak references são usadas em caches ou índices.

Coleta de lixo e testes

Em CPython, objetos sem ciclos costumam ser destruídos assim que a última referência forte desaparece. Outras implementações podem coletar em outro momento. O módulo gc da biblioteca padrão permite inspecionar e solicitar uma coleta em testes.

import gc
import weakref

class Item:
    pass

item = Item()
referencia = weakref.ref(item)
del item
gc.collect()
assert referencia() is None

Não dependa de gc.collect() como parte do fluxo normal para esconder retenções de memória. Ele é útil em diagnóstico e testes controlados, não como substituto para um modelo de propriedade claro.

Referências fracas não resolvem ciclos sozinhas

O coletor de lixo já consegue lidar com muitos ciclos de objetos. Weak references são úteis quando uma relação conceitualmente não representa propriedade. Por exemplo, um filho pode manter uma referência fraca ao pai quando o pai já possui fortemente o filho. Isso reduz ciclos e deixa explícito quem controla o ciclo de vida.

Não transforme todas as ligações em weak references. O código precisa saber quem mantém cada objeto vivo. Se nenhuma parte assumir essa responsabilidade, valores podem desaparecer cedo demais e produzir falhas difíceis de reproduzir.

Erros comuns

  • Usar um cache fraco como armazenamento persistente.
  • Guardar apenas referências fracas e esperar que os objetos permaneçam vivos.
  • Verificar a referência duas vezes em código concorrente.
  • Capturar o referente dentro do callback ou finalizador.
  • Usar um método vinculado da própria instância em finalize().
  • Esquecer "__weakref__" em uma classe com __slots__.
  • Esperar suporte direto em listas, dicionários, inteiros ou tuplas.
  • Ignorar igualdade personalizada em WeakKeyDictionary.
  • Assumir que a coleta ocorre imediatamente em qualquer implementação Python.

Boas práticas

  • Use weak references somente em relações que não representam propriedade.
  • Prefira WeakValueDictionary, WeakKeyDictionary, WeakSet ou finalize() às APIs de baixo nível.
  • Recupere o referente uma única vez antes de usá-lo.
  • Mantenha callbacks pequenos, externos e sem referências ao objeto monitorado.
  • Documente que entradas de caches fracos podem desaparecer.
  • Teste o comportamento após apagar referências fortes.
  • Meça memória antes e depois da mudança.
  • Combine weak references com limites, expiração e sincronização quando necessário.

Conclusão

O weakref no Python permite observar e indexar objetos sem assumir a responsabilidade por mantê-los vivos. Referências cruas oferecem controle, enquanto WeakValueDictionary, WeakKeyDictionary e WeakSet resolvem caches, metadados e registros de observadores. WeakMethod atende métodos vinculados, e finalize() simplifica limpezas executadas no máximo uma vez.

A ferramenta funciona melhor quando a propriedade dos objetos está clara. Uma referência forte deve existir enquanto o objeto realmente for necessário; a referência fraca serve apenas como acesso auxiliar. Com callbacks sem ciclos, suporte correto a __slots__ e testes de ciclo de vida, o módulo ajuda a reduzir retenções de memória sem transformar o gerenciamento de objetos em comportamento imprevisível.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Ícone de arquivo ZIP para artigo sobre zipfile no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipfile no Python: arquivos ZIP seguros

    Aprenda a criar, ler, validar e extrair arquivos ZIP com zipfile no Python de forma previsível e segura.

    Ler mais

    Tempo de leitura: 5 minutos
    27/07/2026
    Grafo de dependências e fluxo de tarefas em Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    graphlib no Python: ordenação topológica

    Aprenda graphlib no Python para ordenar dependências, detectar ciclos e executar tarefas independentes em paralelo.

    Ler mais

    Tempo de leitura: 6 minutos
    27/07/2026
    Código Python para contexto seguro em aplicações assíncronas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextvars no Python: contexto seguro

    Aprenda a usar contextvars no Python para isolar contexto em asyncio, logs, threads e testes sem depender de variáveis globais.

    Ler mais

    Tempo de leitura: 7 minutos
    26/07/2026
    Código Python usando cached_property para armazenar cálculos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    cached_property no Python: cache em objetos

    Aprenda cached_property no Python para armazenar cálculos caros, invalidar valores e evitar caches desatualizados em objetos.

    Ler mais

    Tempo de leitura: 7 minutos
    25/07/2026
    Código Python com funções especializadas por tipo
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    singledispatch no Python: polimorfismo simples

    Aprenda singledispatch no Python para criar funções por tipo, reduzir isinstance e organizar polimorfismo com exemplos práticos.

    Ler mais

    Tempo de leitura: 6 minutos
    25/07/2026
    Código Python com descriptors e atributos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Descriptors em Python: guia prático

    Aprenda descriptors em Python com __get__, __set__, validação, property, armazenamento por instância, testes e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    22/07/2026