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.
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.
42
NoneYou 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.
def on_collect(ref):
print("Object collected")
obj = Data(100)
weak = weakref.ref(obj, on_collect)
del objThe 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.
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.
True
FalseNo 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.
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.
2
1This 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.
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.
{'created': 'today'}
1
0Unlike 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.
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 rThe 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.
Cleaning up db_connectionThe 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
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.
Frequently Asked Questions
When should I use weak references instead of regular references?
What is the difference between WeakValueDictionary and a regular dict?
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.
More in this topic
How to Test Python Code
Learn what testing means in Python, the core ideas behind automated tests, and the built-in and third-party tools that make testing practical.
Write Your First Python Unit Test
Write and run your first Python unit test step by step, using unittest to check a small function and understand the test output.
The Factory Pattern in Python
Learn how to use the factory pattern in Python to encapsulate object creation, making your code more flexible and easier to extend.