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

    Código Python em um notebook para criar dataclasses dinâmicas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    make_dataclass decorator: transforme dataclasses

    Aprenda a usar o parâmetro decorator de make_dataclass para validar, registrar e transformar dataclasses dinâmicas no Python.

    Ler mais

    Tempo de leitura: 6 minutos
    23/10/2026
    Estrutura de código e XML para canonicalização com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ElementTree canonicalize: normalize XML no Python

    Aprenda ElementTree canonicalize no Python para comparar XML, gerar hashes estáveis e controlar namespaces, espaços e comentários.

    Ler mais

    Tempo de leitura: 5 minutos
    22/10/2026
    Código de programação Python em uma tela
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    T-strings e templatelib: interpolação estruturada

    Aprenda t-strings e string.templatelib no Python para processar interpolações, criar renderizadores seguros e preservar dados estruturados.

    Ler mais

    Tempo de leitura: 6 minutos
    22/10/2026
    Banco SQLite e código Python para exportação seletiva com iterdump
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SQLite iterdump: crie dumps seletivos no Python

    Aprenda sqlite3.Connection.iterdump com filtro para gerar dumps seletivos, validar dependências e restaurar bancos com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    21/10/2026
    Código e arquivos representando compressão de dados no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard rápido no Python

    Aprenda a usar compression.zstd no Python para comprimir e descomprimir dados com desempenho, streaming e segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    21/10/2026
    Arquivo de configuração e estrutura de código para ConfigParser no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ConfigParser: leia INI sem seção no Python

    Aprenda a ler arquivos INI sem cabeçalho usando ConfigParser, com validação, tipos, compatibilidade e escrita segura.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026