Weak References in Python

Learn how to use weak references with the weakref module to build caches, registries, and observer patterns that do not prevent garbage collection.

8 min read

Weak references in Python let you refer to an object without preventing it from being garbage collected. A normal reference increases the object's reference count and keeps it alive, while a weak reference does not. When all strong references to an object are gone, the object is collected even if weak references to it still exist.

The article on Python memory management covered how reference counting and garbage collection work. Weak references are the tool you use when you need to track an object without owning it, which prevents the reference cycles and memory leaks that strong references can cause.

The weakref module in the standard library provides everything you need: single weak references, weak dictionaries, weak sets, and finalization callbacks.

Creating and using a single weak reference

The ref function creates a weak reference to any object. To access the referenced object, call the weak reference like a function. It returns the object if it still exists, or None if it has been collected.

pythonpython
import weakref
 
class Data:
    def __init__(self, value):
        self.value = value
 
obj = Data(42)
weak = weakref.ref(obj)
print(weak().value)
 
del obj
print(weak())

The weak reference returns the object while it is alive. After the strong reference is deleted, the weak reference returns None.

texttext
42
None

You can also register a callback that fires when the object is about to be destroyed. The callback receives the weak reference itself as an argument.

pythonpython
def on_collect(ref):
    print("Object collected")
 
obj = Data(100)
weak = weakref.ref(obj, on_collect)
del obj

The callback runs during garbage collection, after the last strong reference is gone. This provides a clean hook for resource cleanup without the problems that the del method has with reference cycles.

WeakValueDictionary for automatic cache eviction

A WeakValueDictionary holds weak references to its values. When nothing else references a value, the entry is automatically removed. This makes it ideal for caches that should not grow unbounded.

pythonpython
cache = weakref.WeakValueDictionary()
 
class Result:
    def __init__(self, data):
        self.data = data
 
result = Result([1, 2, 3])
cache["key"] = result
print("key" in cache)
 
del result
print("key" in cache)

The entry survives as long as the strong reference to the result object exists anywhere in the program. Once the last strong reference is deleted, the cache entry disappears automatically without any manual eviction.

texttext
True
False

No explicit invalidation or size management is needed. The garbage collector handles eviction automatically. This is simpler and safer than implementing TTL-based expiration or manual cache pruning.

WeakSet for tracking objects without owning them

A WeakSet holds weak references to its elements. You can add objects, test membership, and iterate over the set. Elements are automatically removed when collected.

pythonpython
tracked = weakref.WeakSet()
 
class Observer:
    def __init__(self, name):
        self.name = name
 
obs1 = Observer("A")
obs2 = Observer("B")
tracked.add(obs1)
tracked.add(obs2)
print(len(tracked))
 
del obs1
print(len(tracked))

The set automatically drops the collected observer without any manual unregistration calls. This pattern ensures the tracking system never accidentally prevents garbage collection of observer instances.

texttext
2
1

This pattern is useful for event systems where subscribers come and go. Publishers can hold weak references to subscribers and never worry about leaking memory when subscribers are no longer needed.

WeakKeyDictionary for identity-based lookups

A WeakKeyDictionary holds weak references to its keys. When a key object is collected, the entry is removed. This is useful when you want to attach metadata to objects without preventing their normal lifecycle.

pythonpython
metadata = weakref.WeakKeyDictionary()
 
class Target:
    pass
 
t = Target()
metadata[t] = {"created": "today"}
print(metadata[t])
print(len(metadata))
 
del t
print(len(metadata))

The metadata is available through the dictionary while the target object remains alive anywhere in the program. After the target is garbage collected and no strong references remain, the entry disappears automatically with no cleanup needed.

texttext
{'created': 'today'}
1
0

Unlike WeakValueDictionary, the keys are weakly held here. This is appropriate when the key objects are the ones whose lifecycle you want to respect, while the values can be any type.

The finalize function for cleanup callbacks

The finalize function registers a callback that runs when an object is garbage collected. It is safer than the del method because it handles reference cycles correctly.

pythonpython
import weakref
 
class Resource:
    def __init__(self, name):
        self.name = name
 
def cleanup(name):
    print(f"Cleaning up {name}")
 
r = Resource("db_connection")
weakref.finalize(r, cleanup, "db_connection")
del r

The finalize callback fires when the object is collected by the garbage collector, even if the object is part of a complex reference cycle that would prevent the del method from running.

texttext
Cleaning up db_connection

The finalize function returns a callable finalizer object. You can call finalizer.detach() to cancel the callback if cleanup is no longer needed. This is useful when the resource is explicitly closed before the object is collected.

The article on building a Python library with advanced features covers how weak references and finalization fit into larger library design patterns.

Rune AI

Rune AI

Key Insights

  • Weak references do not increase an object's reference count; the object can be collected while weak refs exist.
  • WeakValueDictionary automatically removes entries when values are garbage collected.
  • Use finalize for cleanup callbacks that work correctly with reference cycles.
  • Weak references require objects to be weakly referenceable; most built-in types qualify.
  • Callbacks registered via weakref.ref are called when the referenced object is about to be destroyed.
RunePowered by Rune AI

Frequently Asked Questions

When should I use weak references instead of regular references?

Use weak references when you need to track an object without owning it. This is common in caches where entries should be evicted when nothing else references the value, in observer patterns where subjects should not keep observers alive, and in registries where you want to discover objects without preventing their normal lifecycle. If you just need a regular data structure, use a normal reference.

What is the difference between WeakValueDictionary and a regular dict?

A WeakValueDictionary holds weak references to its values instead of strong references. When nothing else in the program references a value, the entry is automatically removed from the dictionary. A regular dict keeps values alive as long as the dict itself exists. WeakValueDictionary is ideal for caches where you want entries to disappear automatically when they are no longer needed.

Conclusion

Weak references give you the ability to reference objects without owning them. The weakref module provides the tools: ref for individual weak references, WeakValueDictionary and WeakKeyDictionary for mappings, WeakSet for collections, and finalize for cleanup callbacks. Use them for caches, registries, and observer patterns where strong references would create unwanted object retention.