The weakref module creates references to objects that don't prevent garbage collection. When you need to reference an object without keeping it alive, weak references are the answer.
The Problem
Normal references keep objects alive:
class ExpensiveObject:
def __init__(self, data):
self.data = data
cache = {}
obj = ExpensiveObject("large data")
cache['key'] = obj # Strong reference in cache
del obj # Object still alive because cache holds referenceThe object can't be garbage collected until removed from the cache.
Weak References
Weak references don't prevent collection:
import weakref
class ExpensiveObject:
def __init__(self, data):
self.data = data
obj = ExpensiveObject("large data")
weak_ref = weakref.ref(obj)
# Access the object
print(weak_ref()) # <ExpensiveObject object>
del obj # Now it can be garbage collected
print(weak_ref()) # NoneWeakValueDictionary
A dictionary that doesn't keep values alive:
import weakref
class User:
def __init__(self, name):
self.name = name
cache = weakref.WeakValueDictionary()
def get_user(user_id):
if user_id in cache:
return cache[user_id]
user = User(f"User {user_id}")
cache[user_id] = user
return user
u = get_user(1)
print(len(cache)) # 1
del u # User can be collected
# After GC, cache[1] disappears automaticallyWeakKeyDictionary
Store data associated with objects without keeping them alive:
import weakref
class Document:
pass
metadata = weakref.WeakKeyDictionary()
doc = Document()
metadata[doc] = {"views": 100, "modified": True}
# Access metadata while doc exists
print(metadata[doc])
del doc # Document collected, metadata entry removedWeakSet
A set that doesn't keep members alive:
import weakref
class Observer:
def __init__(self, name):
self.name = name
observers = weakref.WeakSet()
o1 = Observer("first")
o2 = Observer("second")
observers.add(o1)
observers.add(o2)
print(len(observers)) # 2
del o1
# After GC, observers only contains o2Callbacks on Collection
Run code when an object is collected:
import weakref
class Resource:
def __init__(self, name):
self.name = name
def on_collected(ref):
print(f"Resource was collected")
obj = Resource("important")
weak_ref = weakref.ref(obj, on_collected)
del obj # Prints: Resource was collectedPractical Example: Caching
Cache expensive computations without memory leaks:
import weakref
class ImageProcessor:
_cache = weakref.WeakValueDictionary()
@classmethod
def get_processed(cls, image_id):
if image_id in cls._cache:
return cls._cache[image_id]
# Expensive processing
result = cls._process(image_id)
cls._cache[image_id] = result
return result
@classmethod
def _process(cls, image_id):
# Simulate expensive work
return ProcessedImage(image_id)When ProcessedImage objects are no longer used elsewhere, they're automatically removed from the cache.
Observer Pattern
Implement observers without memory leaks:
import weakref
class Subject:
def __init__(self):
self._observers = weakref.WeakSet()
def attach(self, observer):
self._observers.add(observer)
def notify(self, message):
for observer in self._observers:
observer.update(message)
class Observer:
def update(self, message):
print(f"Received: {message}")
subject = Subject()
obs = Observer()
subject.attach(obs)
subject.notify("hello") # Works
del obs
subject.notify("world") # obs is gone, no notification sentFinalize
Run cleanup code when objects are collected:
import weakref
class TempFile:
def __init__(self, path):
self.path = path
self._finalizer = weakref.finalize(
self, self._cleanup, path
)
@staticmethod
def _cleanup(path):
print(f"Cleaning up {path}")
# os.unlink(path)
tmp = TempFile("/tmp/data.txt")
del tmp # Prints: Cleaning up /tmp/data.txtLimitations
Not all objects support weak references:
import weakref
# These work
weakref.ref(object())
weakref.ref([]) # Error! list doesn't support weakref
# Built-in types like int, str, list, dict, tuple
# don't support weak references by defaultCustom classes support weak references unless __slots__ excludes __weakref__.
When to Use weakref
Use weak references when:
- Building caches that shouldn't prevent GC
- Implementing observer patterns
- Storing metadata about objects you don't own
- Breaking reference cycles
Don't use them when:
- You need to guarantee the object stays alive
- Working with built-in types that don't support them
- The complexity isn't worth the memory savings
Weak references are a specialized tool for memory management. When you need objects to be collectible while still being referenceable, they're exactly what you need.