weakref.finalize: limpeza automática sem reter objetos

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

Recursos externos como arquivos temporários, handles nativos, sockets auxiliares e registros em caches precisam ser liberados. O caminho principal deve usar context managers e métodos explícitos, mas algumas bibliotecas também precisam de uma rede de segurança quando o objeto é coletado sem fechamento. weakref.finalize registra um callback que permanece vivo sem manter o objeto observado vivo e é executado quando esse objeto se torna inalcançável.

Neste guia, você aprenderá a criar finalizers, evitar referências fortes acidentais, executar a limpeza antecipadamente, usar detach(), verificar alive, entender o comportamento no encerramento do interpretador e decidir quando não depender da coleta de lixo.

Primeiro finalizer

import weakref

class Recurso:
    pass

def limpar(nome):
    print("limpando", nome)

recurso = Recurso()
finalizer = weakref.finalize(recurso, limpar, "temporário")

Enquanto recurso estiver vivo, o callback não é chamado. Quando o objeto se torna inalcançável, o finalizer pode executar limpar("temporário").

O finalizer não mantém o objeto vivo

A associação usa referência fraca. Contudo, os argumentos do callback são mantidos fortemente. Se algum argumento referencia o próprio objeto, você cria uma cadeia que pode impedir a coleta.

# Evite isto:
weakref.finalize(recurso, recurso.fechar)

Um método vinculado mantém referência forte à instância. Prefira uma função independente que receba apenas os dados necessários:

weakref.finalize(recurso, fechar_handle, recurso.handle)

Callback idempotente

A limpeza deve tolerar chamadas repetidas ou coordenar estado para que o recurso seja liberado uma única vez. Embora o mesmo objeto finalizer execute seu callback no máximo uma vez, o código também pode oferecer close() explícito.

Executar antecipadamente

finalizer()

Chamar o objeto finalizer executa o callback imediatamente se ainda estiver ativo. O valor retornado pelo callback é devolvido. Chamadas posteriores não executam novamente.

Propriedade alive

if finalizer.alive:
    finalizer()

alive indica se o callback ainda está registrado e não foi executado nem destacado.

detach

dados = finalizer.detach()

detach() desativa o finalizer e, se ele estava vivo, devolve uma tupla contendo objeto, função, argumentos e kwargs. Use-o para transferir responsabilidade de limpeza para outro componente.

peek

dados = finalizer.peek()

peek() inspeciona o registro sem desativá-lo. O resultado pode conter uma referência forte temporária ao objeto; descarte-a rapidamente.

Finalizer e close explícito

class ArquivoTemporario:
    def __init__(self, caminho):
        self.caminho = caminho
        self._finalizer = weakref.finalize(
            self,
            remover_arquivo,
            caminho,
        )

    def close(self):
        self._finalizer()

O método explícito oferece liberação determinística. O finalizer atua somente como fallback.

Context manager continua preferível

with ArquivoTemporario(caminho) as recurso:
    usar(recurso)

Um context manager garante limpeza no fim do bloco mesmo quando ocorre exceção. A coleta de lixo não possui prazo garantido em todas as implementações e situações.

Por que não usar __del__

__del__ pode complicar herança, ciclos, exceções e estado parcial do interpretador. weakref.finalize separa a função de limpeza do objeto e oferece controle por alive, chamada e detach.

Tempo de execução não garantido

Não baseie correção crítica no momento em que o callback será chamado. Referências podem permanecer vivas em caches, closures, tracebacks ou threads. Em outras implementações de Python, a coleta pode ocorrer mais tarde que no CPython.

Coleta cíclica

Finalizers foram projetados para funcionar de forma mais robusta com o coletor, mas o callback ainda deve evitar ressuscitar objetos ou depender de uma ordem específica entre vários recursos.

Ordem de finalização

Quando vários objetos ficam inalcançáveis juntos, não construa uma cadeia que exija ordem implícita. Modele dependências explicitamente: um gerenciador deve fechar filhos antes de si mesmo.

Encerramento do interpretador

Finalizers vivos podem ser executados na saída, normalmente em ordem inversa de criação. A propriedade atexit controla essa participação:

finalizer.atexit = False

No shutdown, módulos e globais podem já estar parcialmente desmontados. Passe ao callback funções e valores independentes em vez de procurar nomes globais tardiamente.

Exceções no callback

Exceções de finalização não podem ser propagadas de forma normal ao código que causou a coleta. Mantenha callbacks pequenos, capture falhas esperadas e registre com segurança. Não faça operações complexas de rede como única forma de persistir dados importantes.

Threads

O callback pode ocorrer em um contexto que não corresponde à thread que criou o objeto. Evite assumir thread-local state. Se um recurso exige liberação em uma thread específica, agende a ação explicitamente no executor correto.

Asyncio

Um finalizer é síncrono e não pode executar await. Não tente fechar diretamente um recurso assíncrono com coroutine. Ofereça async with e aclose(); no máximo, use o finalizer para emitir aviso ou sinalizar um loop ainda ativo com extremo cuidado.

Arquivos temporários

from pathlib import Path
import weakref

class Artefato:
    def __init__(self, caminho: Path):
        self.caminho = caminho
        self._cleanup = weakref.finalize(
            self,
            Path.unlink,
            caminho,
            missing_ok=True,
        )

    def remover(self):
        self._cleanup()

A função e o caminho não referenciam a instância. missing_ok=True torna a operação idempotente quando o arquivo já foi removido.

Handles nativos

Ao trabalhar com C, mantenha no callback apenas o identificador necessário e uma função segura para liberá-lo. Verifique se a biblioteca nativa ainda está carregada durante o shutdown.

Objetos em caches

Se um cache mantém referência forte ao objeto, o finalizer não executa. Considere WeakValueDictionary ou uma política explícita de eviction. O artigo sobre weakref no Python explica caches fracos.

Testando finalização

Testes não devem depender apenas de gc.collect(). Teste o caminho explícito chamando close() ou o próprio finalizer e verifique idempotência. Um teste separado pode confirmar o fallback com referência fraca.

finalizer()
assert not finalizer.alive
finalizer()  # não repete

Evite capturar self em closure

# Incorreto: a closure captura self
weakref.finalize(self, lambda: liberar(self.handle))

Copie o handle para uma variável independente e não capture a instância.

Transferindo propriedade

Quando um recurso passa para outro objeto, use detach() no proprietário antigo e registre um novo finalizer no novo proprietário. Isso evita duas limpezas concorrentes.

Observabilidade

Um callback fallback pode emitir uma métrica indicando que o recurso não foi fechado explicitamente. Evite logs ruidosos em shutdown e não inclua segredos ou objetos completos.

Desempenho

Finalizers possuem custo de registro e manutenção. Não crie um para cada objeto minúsculo de alta frequência se a limpeza puder ser gerenciada em lote por um proprietário maior.

Erros comuns

  • Passar método vinculado do objeto: mantém a instância viva.
  • Usar como limpeza principal: prefira close e context manager.
  • Executar coroutine no callback: finalizer é síncrono.
  • Depender da ordem de vários callbacks: modele a dependência explicitamente.
  • Usar globais no shutdown: módulos podem estar desmontados.
  • Ignorar idempotência: caminhos explícito e fallback precisam coordenar.

Exemplo completo: recurso com fallback

import weakref

class ConexaoNativa:
    def __init__(self, api):
        handle = api.abrir()
        self._api = api
        self._handle = handle
        self._finalizer = weakref.finalize(
            self,
            api.fechar,
            handle,
        )

    @property
    def fechada(self):
        return not self._finalizer.alive

    def close(self):
        self._finalizer()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc, tb):
        self.close()

O uso normal é determinístico com with. Se o consumidor esquecer, o finalizer ainda tenta liberar o handle sem referenciar a instância.

Conclusão

weakref.finalize oferece uma rede de segurança controlável para limpeza associada ao ciclo de vida de um objeto. Ele evita várias fragilidades de __del__, mas não transforma coleta de lixo em gerenciamento determinístico.

A documentação oficial de weakref.finalize detalha a API. Use context managers como caminho principal, não capture o objeto no callback e mantenha a finalização pequena, idempotente e independente do shutdown.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SimpleNamespace: objetos leves com atributos

    Aprenda SimpleNamespace no Python para criar objetos leves por atributos, converter dicionários e escolher entre dataclass e TypedDict.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Close-up of a detailed South America map showcasing geography and cartography.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ChainMap no Python: mapas em camadas

    Aprenda ChainMap no Python para combinar configurações e escopos em camadas, controlar precedência, escrita e snapshots.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up of a python snake curled up, showcasing its detailed scales.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    itertools.pairwise: analise pares consecutivos

    Aprenda itertools.pairwise no Python para analisar pares consecutivos, calcular deltas, detectar transições e validar sequências.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    itertools.batched: processe iteráveis em lotes

    Aprenda itertools.batched no Python para processar iteráveis em lotes, controlar memória, usar strict e criar pipelines resilientes.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    cmp_to_key: adapte comparadores antigos ao sorted

    Aprenda cmp_to_key no Python para adaptar comparadores antigos, ordenar com locale, preservar estabilidade e evitar regras inconsistentes.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    total_ordering: gere comparações consistentes

    Aprenda total_ordering no Python para gerar comparações consistentes, usar NotImplemented, integrar dataclasses e testar ordens.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026