Introduction
In Python, memory management is mostly handled by the garbage collector. But in some advanced use cases, we need to keep track of objects without preventing them from being garbage-collected.
This is where weak references come into play.
In this post, we’ll explore:
What weak references are and how they work
How they help avoid memory leaks
When and why you should use them
Practical examples using weakref
Best practices for using weak references
1️⃣ What Are Weak References?
A weak reference is a reference to an object that doesn’t increase its reference count. This means:
If there are only weak references to an object, it can still be garbage-collected.Weak references are useful for caching, tracking, or observing objects without keeping them alive.If there are only weak references to an object, it can still be garbage-collected. Weak references are useful for caching, tracking, or observing objects without keeping them alive.If there are only weak references to an object, it can still be garbage-collected. Weak references are useful for caching, tracking, or observing objects without keeping them alive.
Enter fullscreen mode Exit fullscreen mode
Contrast with a normal (strong) reference:
<span>a</span> <span>=</span> <span>SomeObject</span><span>()</span> <span># Strong reference → keeps object alive </span><span>a</span> <span>=</span> <span>SomeObject</span><span>()</span> <span># Strong reference → keeps object alive </span>a = SomeObject() # Strong reference → keeps object alive
Enter fullscreen mode Exit fullscreen mode
With weak reference:
<span>import</span> <span>weakref</span><span>obj</span> <span>=</span> <span>SomeObject</span><span>()</span><span>w</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>obj</span><span>)</span> <span># Weak reference </span><span>print</span><span>(</span><span>w</span><span>())</span> <span># Returns the object while it's alive </span><span>del</span> <span>obj</span><span>print</span><span>(</span><span>w</span><span>())</span> <span># Returns None if object is collected </span><span>import</span> <span>weakref</span> <span>obj</span> <span>=</span> <span>SomeObject</span><span>()</span> <span>w</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>obj</span><span>)</span> <span># Weak reference </span> <span>print</span><span>(</span><span>w</span><span>())</span> <span># Returns the object while it's alive </span><span>del</span> <span>obj</span> <span>print</span><span>(</span><span>w</span><span>())</span> <span># Returns None if object is collected </span>import weakref obj = SomeObject() w = weakref.ref(obj) # Weak reference print(w()) # Returns the object while it's alive del obj print(w()) # Returns None if object is collected
Enter fullscreen mode Exit fullscreen mode
2️⃣ Why Use Weak References?
To avoid memory leaks in long-running applications
To build caches that auto-expire when the objects are no longer used
To track objects without preventing their destruction
To store metadata about objects without owning them
3️⃣ Using weakref.ref() – The Basics
<span>import</span> <span>weakref</span><span>class</span> <span>Person</span><span>:</span><span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>name</span><span>):</span><span>self</span><span>.</span><span>name</span> <span>=</span> <span>name</span><span>p</span> <span>=</span> <span>Person</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span><span>weak_p</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>p</span><span>)</span><span>print</span><span>(</span><span>weak_p</span><span>)</span> <span># <weakref at 0x...; to 'Person' at 0x...> </span><span>print</span><span>(</span><span>weak_p</span><span>())</span> <span># <__main__.Person object at ...> </span><span>print</span><span>(</span><span>weak_p</span><span>().</span><span>name</span><span>)</span> <span># Alice </span><span>del</span> <span>p</span><span>print</span><span>(</span><span>weak_p</span><span>())</span> <span># None – object has been garbage collected </span><span>import</span> <span>weakref</span> <span>class</span> <span>Person</span><span>:</span> <span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>name</span><span>):</span> <span>self</span><span>.</span><span>name</span> <span>=</span> <span>name</span> <span>p</span> <span>=</span> <span>Person</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span> <span>weak_p</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>p</span><span>)</span> <span>print</span><span>(</span><span>weak_p</span><span>)</span> <span># <weakref at 0x...; to 'Person' at 0x...> </span><span>print</span><span>(</span><span>weak_p</span><span>())</span> <span># <__main__.Person object at ...> </span><span>print</span><span>(</span><span>weak_p</span><span>().</span><span>name</span><span>)</span> <span># Alice </span> <span>del</span> <span>p</span> <span>print</span><span>(</span><span>weak_p</span><span>())</span> <span># None – object has been garbage collected </span>import weakref class Person: def __init__(self, name): self.name = name p = Person("Alice") weak_p = weakref.ref(p) print(weak_p) # <weakref at 0x...; to 'Person' at 0x...> print(weak_p()) # <__main__.Person object at ...> print(weak_p().name) # Alice del p print(weak_p()) # None – object has been garbage collected
Enter fullscreen mode Exit fullscreen mode
Key point: The weak reference does not prevent garbage collection.
4️⃣ Using weakref.WeakKeyDictionary
A dictionary where keys are stored as weak references. When the object is deleted, the key is removed automatically.
<span>import</span> <span>weakref</span><span>class</span> <span>Data</span><span>:</span><span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>value</span><span>):</span><span>self</span><span>.</span><span>value</span> <span>=</span> <span>value</span><span>data1</span> <span>=</span> <span>Data</span><span>(</span><span>10</span><span>)</span><span>data2</span> <span>=</span> <span>Data</span><span>(</span><span>20</span><span>)</span><span>wk_dict</span> <span>=</span> <span>weakref</span><span>.</span><span>WeakKeyDictionary</span><span>()</span><span>wk_dict</span><span>[</span><span>data1</span><span>]</span> <span>=</span> <span>"</span><span>Object 1</span><span>"</span><span>wk_dict</span><span>[</span><span>data2</span><span>]</span> <span>=</span> <span>"</span><span>Object 2</span><span>"</span><span>print</span><span>(</span><span>dict</span><span>(</span><span>wk_dict</span><span>))</span> <span># {data1: ..., data2: ...} </span><span>del</span> <span>data1</span><span>print</span><span>(</span><span>dict</span><span>(</span><span>wk_dict</span><span>))</span> <span># {data2: ...} – data1 key removed automatically </span><span>import</span> <span>weakref</span> <span>class</span> <span>Data</span><span>:</span> <span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>value</span><span>):</span> <span>self</span><span>.</span><span>value</span> <span>=</span> <span>value</span> <span>data1</span> <span>=</span> <span>Data</span><span>(</span><span>10</span><span>)</span> <span>data2</span> <span>=</span> <span>Data</span><span>(</span><span>20</span><span>)</span> <span>wk_dict</span> <span>=</span> <span>weakref</span><span>.</span><span>WeakKeyDictionary</span><span>()</span> <span>wk_dict</span><span>[</span><span>data1</span><span>]</span> <span>=</span> <span>"</span><span>Object 1</span><span>"</span> <span>wk_dict</span><span>[</span><span>data2</span><span>]</span> <span>=</span> <span>"</span><span>Object 2</span><span>"</span> <span>print</span><span>(</span><span>dict</span><span>(</span><span>wk_dict</span><span>))</span> <span># {data1: ..., data2: ...} </span> <span>del</span> <span>data1</span> <span>print</span><span>(</span><span>dict</span><span>(</span><span>wk_dict</span><span>))</span> <span># {data2: ...} – data1 key removed automatically </span>import weakref class Data: def __init__(self, value): self.value = value data1 = Data(10) data2 = Data(20) wk_dict = weakref.WeakKeyDictionary() wk_dict[data1] = "Object 1" wk_dict[data2] = "Object 2" print(dict(wk_dict)) # {data1: ..., data2: ...} del data1 print(dict(wk_dict)) # {data2: ...} – data1 key removed automatically
Enter fullscreen mode Exit fullscreen mode
Use Case: Managing metadata without holding onto large objects.
5️⃣ Using weakref.WeakValueDictionary
A dictionary where values are weak references.
<span>import</span> <span>weakref</span><span>class</span> <span>User</span><span>:</span><span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>username</span><span>):</span><span>self</span><span>.</span><span>username</span> <span>=</span> <span>username</span><span>u1</span> <span>=</span> <span>User</span><span>(</span><span>"</span><span>alice</span><span>"</span><span>)</span><span>u2</span> <span>=</span> <span>User</span><span>(</span><span>"</span><span>bob</span><span>"</span><span>)</span><span>users</span> <span>=</span> <span>weakref</span><span>.</span><span>WeakValueDictionary</span><span>()</span><span>users</span><span>[</span><span>"</span><span>a</span><span>"</span><span>]</span> <span>=</span> <span>u1</span><span>users</span><span>[</span><span>"</span><span>b</span><span>"</span><span>]</span> <span>=</span> <span>u2</span><span>print</span><span>(</span><span>users</span><span>[</span><span>"</span><span>a</span><span>"</span><span>].</span><span>username</span><span>)</span> <span># alice </span><span>del</span> <span>u1</span><span>print</span><span>(</span><span>"</span><span>a</span><span>"</span> <span>in</span> <span>users</span><span>)</span> <span># False – value was garbage-collected </span><span>import</span> <span>weakref</span> <span>class</span> <span>User</span><span>:</span> <span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>username</span><span>):</span> <span>self</span><span>.</span><span>username</span> <span>=</span> <span>username</span> <span>u1</span> <span>=</span> <span>User</span><span>(</span><span>"</span><span>alice</span><span>"</span><span>)</span> <span>u2</span> <span>=</span> <span>User</span><span>(</span><span>"</span><span>bob</span><span>"</span><span>)</span> <span>users</span> <span>=</span> <span>weakref</span><span>.</span><span>WeakValueDictionary</span><span>()</span> <span>users</span><span>[</span><span>"</span><span>a</span><span>"</span><span>]</span> <span>=</span> <span>u1</span> <span>users</span><span>[</span><span>"</span><span>b</span><span>"</span><span>]</span> <span>=</span> <span>u2</span> <span>print</span><span>(</span><span>users</span><span>[</span><span>"</span><span>a</span><span>"</span><span>].</span><span>username</span><span>)</span> <span># alice </span> <span>del</span> <span>u1</span> <span>print</span><span>(</span><span>"</span><span>a</span><span>"</span> <span>in</span> <span>users</span><span>)</span> <span># False – value was garbage-collected </span>import weakref class User: def __init__(self, username): self.username = username u1 = User("alice") u2 = User("bob") users = weakref.WeakValueDictionary() users["a"] = u1 users["b"] = u2 print(users["a"].username) # alice del u1 print("a" in users) # False – value was garbage-collected
Enter fullscreen mode Exit fullscreen mode
Use Case: Auto-expiring caches for objects that shouldn’t stay in memory forever.
6️⃣ Weak References with Callbacks
You can attach a callback to a weak reference. This is useful for cleanup or logging.
<span>import</span> <span>weakref</span><span>class</span> <span>Item</span><span>:</span><span>def</span> <span>__del__</span><span>(</span><span>self</span><span>):</span><span>print</span><span>(</span><span>"</span><span>Item deleted</span><span>"</span><span>)</span><span>def</span> <span>on_finalize</span><span>(</span><span>wr</span><span>):</span><span>print</span><span>(</span><span>"</span><span>Object has been garbage collected!</span><span>"</span><span>)</span><span>item</span> <span>=</span> <span>Item</span><span>()</span><span>ref</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>item</span><span>,</span> <span>on_finalize</span><span>)</span><span>del</span> <span>item</span><span># Output: # Item deleted # Object has been garbage collected! </span><span>import</span> <span>weakref</span> <span>class</span> <span>Item</span><span>:</span> <span>def</span> <span>__del__</span><span>(</span><span>self</span><span>):</span> <span>print</span><span>(</span><span>"</span><span>Item deleted</span><span>"</span><span>)</span> <span>def</span> <span>on_finalize</span><span>(</span><span>wr</span><span>):</span> <span>print</span><span>(</span><span>"</span><span>Object has been garbage collected!</span><span>"</span><span>)</span> <span>item</span> <span>=</span> <span>Item</span><span>()</span> <span>ref</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>item</span><span>,</span> <span>on_finalize</span><span>)</span> <span>del</span> <span>item</span> <span># Output: # Item deleted # Object has been garbage collected! </span>import weakref class Item: def __del__(self): print("Item deleted") def on_finalize(wr): print("Object has been garbage collected!") item = Item() ref = weakref.ref(item, on_finalize) del item # Output: # Item deleted # Object has been garbage collected!
Enter fullscreen mode Exit fullscreen mode
Use Case: Perform cleanup when objects are collected.
7️⃣ Common Pitfalls and Tips
Accessing a dead weak reference
<span>obj</span> <span>=</span> <span>SomeObject</span><span>()</span><span>w</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>obj</span><span>)</span><span>del</span> <span>obj</span><span>print</span><span>(</span><span>w</span><span>())</span> <span># None – object no longer exists </span><span>obj</span> <span>=</span> <span>SomeObject</span><span>()</span> <span>w</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>obj</span><span>)</span> <span>del</span> <span>obj</span> <span>print</span><span>(</span><span>w</span><span>())</span> <span># None – object no longer exists </span>obj = SomeObject() w = weakref.ref(obj) del obj print(w()) # None – object no longer exists
Enter fullscreen mode Exit fullscreen mode
️ Always check if the weak reference is still valid before using it.
Trying to weak-reference an unsupported object
Not all objects can be weakly referenced. For example, int, list, str, and dict usually don’t support weak references.
<span>import</span> <span>weakref</span><span>w</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>42</span><span>)</span> <span># TypeError: cannot create weak reference to 'int' object </span><span>import</span> <span>weakref</span> <span>w</span> <span>=</span> <span>weakref</span><span>.</span><span>ref</span><span>(</span><span>42</span><span>)</span> <span># TypeError: cannot create weak reference to 'int' object </span>import weakref w = weakref.ref(42) # TypeError: cannot create weak reference to 'int' object
Enter fullscreen mode Exit fullscreen mode
️ Only class instances (and custom objects) typically support weak references.
Best Practices
Use weak references when you don’t want to own the object.<p>Avoid memory leaks in observer patterns, plugin systems, and caching layers.</p><p>Always check ref() is not None before accessing the object.<br> </p>Use weak references when you don’t want to own the object. <p>Avoid memory leaks in observer patterns, plugin systems, and caching layers.</p> <p>Always check ref() is not None before accessing the object.<br> </p>Use weak references when you don’t want to own the object.
Avoid memory leaks in observer patterns, plugin systems, and caching layers.
Always check ref() is not None before accessing the object.
Enter fullscreen mode Exit fullscreen mode
8️⃣ Summary
️ Weak references don’t increase reference counts.
️ Used to track objects without preventing their collection.
️ WeakKeyDictionary and WeakValueDictionary manage memory-friendly mappings.
️ Callbacks can be attached to perform cleanup when objects are deleted.
️ Ideal for caches, plugins, and memory-sensitive apps.
What’s Next?
Next, we’ll explore “Mastering Comprehensions in Python – The Pythonic Way to Build Data Structures” Stay tuned.
Got questions? Drop them in the comments!
原文链接:Understanding Weak References in Python – Managing Memory Efficiently
暂无评论内容