weakref en Python: referencias débiles

Publicado el: 28/07/2026
Tempo de leitura: 9 minutos
Módulo de memoria que representa referencias débiles y cachés en Python

La gestión automática de memoria es una de las mayores ventajas de Python, pero las referencias normales pueden mantener objetos vivos durante más tiempo del necesario. Una caché, un registro de observadores o un diccionario de metadatos puede retener objetos grandes aunque el resto de la aplicación ya no los use. El módulo weakref en Python ofrece referencias débiles: enlaces que permiten acceder a un objeto mientras existe sin impedir que el recolector de basura recupere su memoria.

En esta guía aprenderás la diferencia entre referencias fuertes y débiles, cómo usar weakref.ref(), WeakValueDictionary, WeakKeyDictionary, WeakSet, WeakMethod, proxies y finalize(). El tema complementa los artículos sobre descriptors en Python, optimización de scripts lentos, el módulo collections, generadores y evaluación perezosa y yield y generadores eficientes.

Referencias fuertes y ciclo de vida

Una variable común mantiene una referencia fuerte. Mientras exista al menos una referencia fuerte, el objeto continúa vivo. Las variables locales, los atributos, los elementos de listas y los valores de diccionarios crean habitualmente este tipo de referencia.

class Imagen:
    def __init__(self, nombre):
        self.nombre = nombre

imagen = Imagen("portada.png")
cache = {"portada": imagen}
del imagen

print(cache["portada"].nombre)

Eliminar la variable local no destruye el objeto porque el diccionario todavía lo conserva. Este comportamiento suele ser correcto, pero puede ser indeseado cuando el mapeo solo funciona como caché opcional o índice auxiliar. Una referencia débil no aumenta la cantidad de referencias fuertes. Cuando solo quedan referencias débiles, el objeto puede ser recolectado.

Crear una referencia con weakref.ref

weakref.ref() devuelve un objeto invocable. Al llamarlo, recibes el referente si todavía está vivo o None si ya fue eliminado.

import weakref

class Documento:
    pass

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

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

La documentación oficial de weakref denomina referente al objeto señalado por la referencia débil. La propiedad esencial es que la referencia débil no prolonga la vida del referente.

La forma segura de recuperar el objeto

No compruebes una referencia y después la llames de nuevo. En código concurrente, otro hilo podría eliminar la última referencia fuerte entre ambas operaciones. Recupera el objeto una sola vez y conserva esa referencia local durante el uso.

objeto = referencia()
if objeto is None:
    print("El objeto ya no existe")
else:
    objeto.procesar()

Al asignar el resultado a objeto, el programa crea temporalmente una referencia fuerte. Este patrón evita que el referente desaparezca en medio de la operación.

Callbacks al eliminar el objeto

weakref.ref() acepta un callback que se ejecuta cuando el referente está a punto de finalizar. El callback recibe la referencia débil, no el objeto que ya no está disponible.

import weakref

class Sesion:
    pass

def eliminada(referencia):
    print("Sesión eliminada del índice")

sesion = Sesion()
referencia = weakref.ref(sesion, eliminada)
del sesion

No captures el objeto vigilado mediante closure, argumento o método vinculado. Esa referencia fuerte indirecta impediría la recolección. Las excepciones lanzadas por el callback se escriben en la salida de error, pero no pueden propagarse al código que liberó la última referencia fuerte.

Cachés automáticas con WeakValueDictionary

WeakValueDictionary mantiene claves normales y valores débiles. Cuando un valor deja de tener referencias fuertes en otras partes de la aplicación, su entrada desaparece automáticamente.

from weakref import WeakValueDictionary

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

_cache = WeakValueDictionary()

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

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

Este patrón es útil para objetos costosos que pueden reconstruirse: imágenes decodificadas, modelos temporales, wrappers de recursos y representaciones intermedias. La caché es una optimización, no almacenamiento persistente, porque una entrada puede desaparecer cuando ningún consumidor mantiene el objeto.

Cuándo una caché débil no basta

Una caché débil puede vaciarse enseguida si los consumidores no conservan los valores. Tampoco define tamaño máximo, política LRU, caducidad ni consistencia distribuida. functools.lru_cache() puede ser mejor para resultados pequeños y deterministas. Una base de datos o caché externa es apropiada cuando los datos deben sobrevivir a la recolección o al reinicio del proceso.

Elige WeakValueDictionary cuando la regla sea: reutiliza el objeto mientras alguna parte del programa lo necesite realmente, pero no lo mantengas vivo únicamente porque aparece en la caché.

Metadatos externos con WeakKeyDictionary

WeakKeyDictionary mantiene las claves de forma débil. Permite asociar información con objetos de terceros sin modificar esos objetos y sin asumir la propiedad de su ciclo de vida.

from weakref import WeakKeyDictionary

class Conexion:
    pass

metricas = WeakKeyDictionary()
conexion = Conexion()
metricas[conexion] = {"consultas": 3}

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

Este recurso resulta útil en instrumentación, descriptors, frameworks, adaptadores y plugins. Cuando la instancia principal desaparece, sus metadatos auxiliares también se eliminan.

Igualdad e identidad en claves débiles

Existe un detalle importante cuando dos objetos distintos se consideran iguales. Insertar la segunda clave puede reemplazar el valor sin sustituir la identidad de la primera clave mantenida internamente. Cuando la clave original se recolecta, la entrada puede desaparecer aunque el segundo objeto igual siga vivo.

Si son habituales las instancias distintas pero iguales, elimina la entrada anterior antes de asignar la nueva o usa un identificador inmutable. Las clases que personalizan igualdad y hash deben incluir pruebas específicas para este comportamiento.

Registros de observadores con WeakSet

WeakSet implementa la interfaz de conjunto manteniendo elementos débiles. Es una opción natural para observadores, listeners y registros de objetos activos.

from weakref import WeakSet

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

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

for elemento in list(observadores):
    elemento.actualizar("datos modificados")

del observador
print(len(observadores))

Iterar sobre una copia puede ser útil cuando los callbacks modifican el registro. Un conjunto débil evita que el publicador se convierta accidentalmente en propietario de todos los suscriptores.

Métodos vinculados y WeakMethod

Un método vinculado como instancia.actualizar es un objeto temporal creado al acceder al atributo. Una referencia débil normal puede caducar inmediatamente. WeakMethod conserva la información necesaria para reconstruir el método mientras existan la instancia y la función original.

from weakref import WeakMethod

class Vista:
    def actualizar(self):
        print("Actualizando")

vista = Vista()
callback = WeakMethod(vista.actualizar)

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

del vista
print(callback())

Este patrón es especialmente útil en sistemas de eventos, interfaces gráficas y buses de mensajes que no deben retener consumidores indefinidamente.

Proxies débiles

weakref.proxy() crea un proxy que reenvía la mayoría de las operaciones al referente. Evita llamar explícitamente a la referencia, pero después de la recolección cualquier acceso produce ReferenceError.

import weakref

class Configuracion:
    entorno = "producción"

configuracion = Configuracion()
proxy = weakref.proxy(configuracion)
print(proxy.entorno)

del configuracion

try:
    print(proxy.entorno)
except ReferenceError:
    print("Configuración no disponible")

Los proxies nunca son hashable, aunque el referente sí lo sea. En APIs de biblioteca, las referencias explícitas suelen hacer más visible el estado ausente.

Limpieza con weakref.finalize

weakref.finalize() registra una función de limpieza que se ejecuta como máximo una vez cuando el objeto es recolectado. El finalizador se mantiene vivo automáticamente, lo que simplifica su gestión frente a un callback de bajo nivel.

import shutil
import tempfile
import weakref

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

    def eliminar(self):
        self._finalizador()

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

El finalizador puede invocarse explícitamente y las llamadas posteriores no repiten la limpieza. Por defecto, los finalizadores vivos también se ejecutan durante el cierre normal del intérprete en orden inverso a su creación.

No retengas el objeto vigilado

La función y los argumentos pasados a finalize() no deben mantener el objeto vigilado directa ni indirectamente. Pasar un método vinculado de la propia instancia es un error frecuente.

# Evita:
# weakref.finalize(self, self.cerrar)

# Prefiere una función externa con los datos necesarios:
weakref.finalize(self, cerrar_recurso, identificador)

Si el finalizador conserva self, la instancia posee el finalizador, el finalizador posee el método y el método posee la instancia. El objeto puede no llegar a ser recolectable.

Tipos compatibles con referencias débiles

Las instancias de clases Python normales suelen admitir referencias débiles. Las funciones Python, métodos, conjuntos, generadores, sockets, arrays y otros tipos también son compatibles. Las listas y los diccionarios integrados no lo son directamente.

import weakref

class MiLista(list):
    pass

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

Las subclases de list y dict pueden añadir soporte, pero algunos tipos como tuple e int siguen sin admitir referencias débiles incluso al heredarlos.

weakref y __slots__

Declarar __slots__ desactiva el soporte automático para referencias débiles, salvo que la secuencia incluya "__weakref__".

import weakref

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

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

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

Este detalle importa en clases con muchas instancias que utilizan slots para reducir memoria y también participan en cachés o registros débiles.

Recolección de basura y pruebas

En CPython, los objetos sin ciclos suelen destruirse cuando desaparece la última referencia fuerte. Otras implementaciones pueden recolectarlos más tarde. La documentación del módulo estándar gc explica controles y herramientas de diagnóstico.

import gc
import weakref

class Elemento:
    pass

elemento = Elemento()
referencia = weakref.ref(elemento)
del elemento
gc.collect()
assert referencia() is None

Usa gc.collect() en pruebas controladas e investigaciones, no como solución rutinaria para una propiedad de objetos poco clara.

Referencias débiles y ciclos

El recolector cíclico de Python ya resuelve muchos ciclos. Las referencias débiles ayudan cuando una relación no representa propiedad. Por ejemplo, un hijo puede referirse débilmente al padre cuando el padre ya posee fuertemente al hijo. El diseño deja claro qué objeto controla la vida.

No sustituyas todas las relaciones por referencias débiles. Algún componente debe poseer el objeto mientras sea necesario. De lo contrario, los valores pueden desaparecer demasiado pronto y causar fallos intermitentes.

Consideraciones de concurrencia

Una referencia débil puede quedar muerta después de eliminarse la última referencia fuerte. El patrón seguro consiste en llamarla una sola vez y usar el objeto local devuelto. Comprobar la vida con una llamada y recuperar con otra crea una condición de carrera.

Los contenedores débiles tampoco convierten automáticamente operaciones compuestas en atómicas. Si varios hilos actualizan una caché o registro, protege la secuencia completa de lectura, modificación y escritura.

Errores frecuentes

  • Usar una caché débil como almacenamiento persistente.
  • Mantener solo referencias débiles y esperar que los objetos continúen vivos.
  • Llamar dos veces a la referencia en código concurrente.
  • Capturar el referente dentro de su callback o finalizador.
  • Pasar el método vinculado del propio objeto a finalize().
  • Olvidar "__weakref__" en una clase con slots.
  • Esperar soporte directo en listas, diccionarios, enteros o tuplas.
  • Ignorar igualdad personalizada en WeakKeyDictionary.
  • Suponer recolección inmediata en todas las implementaciones Python.

Buenas prácticas

  • Usa referencias débiles solo en relaciones que no representan propiedad.
  • Prefiere contenedores débiles y finalize() a callbacks de bajo nivel.
  • Recupera el referente una sola vez antes de usarlo.
  • Mantén funciones de limpieza externas e independientes de la instancia.
  • Documenta que las entradas de una caché débil pueden desaparecer.
  • Prueba el comportamiento después de eliminar referencias fuertes.
  • Mide memoria antes y después de adoptar weak references.
  • Añade límites, caducidad y sincronización cuando sean necesarios.

Conclusión

weakref en Python permite observar, indexar y reutilizar objetos sin asumir la responsabilidad de mantenerlos vivos. Las referencias básicas ofrecen control de bajo nivel, mientras WeakValueDictionary, WeakKeyDictionary y WeakSet resuelven cachés, metadatos y registros de observadores. WeakMethod trabaja con callbacks vinculados y finalize() proporciona limpieza de una sola ejecución.

El módulo funciona mejor cuando la propiedad es explícita. Las referencias fuertes deben conservar los objetos por necesidades reales de la aplicación; las referencias débiles solo ofrecen acceso auxiliar. Con callbacks sin ciclos, soporte correcto para slots, recuperación segura y pruebas del ciclo de vida, weakref ayuda a reducir retenciones accidentales sin volver impredecible el comportamiento.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Icono de archivo ZIP para un artículo sobre zipfile en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zipfile en Python: archivos ZIP seguros

    Aprende a crear, leer, validar y extraer archivos ZIP con zipfile en Python de forma predecible y segura.

    Ler mais

    Tempo de leitura: 5 minutos
    27/07/2026
    Grafo de dependencias y flujo de tareas en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    graphlib en Python: ordenación topológica

    Aprende graphlib en Python para ordenar dependencias, detectar ciclos y coordinar tareas independientes en paralelo.

    Ler mais

    Tempo de leitura: 6 minutos
    27/07/2026
    Código Python para contexto seguro en aplicaciones asíncronas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    contextvars en Python: contexto seguro

    Aprende a usar contextvars en Python para aislar solicitudes, registros, hilos y tareas asyncio sin variables globales inseguras.

    Ler mais

    Tempo de leitura: 6 minutos
    26/07/2026
    Código Python con cached_property para guardar cálculos costosos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    cached_property en Python: caché en objetos

    Aprende cached_property en Python para guardar cálculos costosos, invalidar valores y evitar cachés desactualizadas.

    Ler mais

    Tempo de leitura: 7 minutos
    25/07/2026
    Código Python con funciones especializadas por tipo
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    singledispatch en Python: funciones por tipo

    Aprende singledispatch en Python para crear funciones por tipo, reducir cadenas isinstance y organizar polimorfismo extensible con ejemplos.

    Ler mais

    Tempo de leitura: 7 minutos
    25/07/2026
    Código Python y atributos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Descriptors en Python: guía práctica

    Aprende descriptors en Python con __get__, __set__, validación, property, almacenamiento por instancia, pruebas, herencia y buenas prácticas.

    Ler mais

    Tempo de leitura: 7 minutos
    22/07/2026