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

    A vibrant collection of blue sewing threads arranged with hands on a white background.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue no Python: coordene threads

    Aprenda queue no Python para coordenar threads com FIFO, prioridade, backpressure, task_done, join, retries e shutdown seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    A male software engineer working on code in a modern office setting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    struct no Python: trabalhe com binário

    Aprenda struct no Python para empacotar dados binários, controlar endianness, usar buffers e validar protocolos e arquivos externos.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile no Python: crie TAR seguro

    Aprenda tarfile no Python para criar TAR comprimido, inspecionar membros e extrair com filtros, limites e proteção contra path traversal.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    Row of colorful office binders neatly arranged on a shelf, ideal for organization concepts.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    gzip no Python: comprima arquivos .gz

    Aprenda gzip no Python para ler e gravar .gz, criar saídas reproduzíveis, trabalhar com streams e limitar a expansão de

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Color-coded office binders organized neatly in a storage shelf, featuring labels and a striking red binder.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    lzma no Python: comprima arquivos XZ

    Aprenda lzma no Python para criar arquivos XZ, usar streams, checks, filtros e limites de memória ao descompactar dados externos.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    Exquisite python skin handbag with intricate snake emblem and elegant design.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    bz2 no Python: comprima com bzip2

    Aprenda bz2 no Python para comprimir arquivos e bytes, processar fluxos em blocos e limitar a expansão de dados externos.

    Ler mais

    Tempo de leitura: 5 minutos
    16/08/2026