weakref module in Python
Been a while, two months and two weeks to be exact, since I wrote the last article. Without further ado, I am writing about weakref
module in Python. Let’s dive in.
The module allows Python programmer to create weak reference to objects. A weak reference is a reference that does not protect the object from getting garbage collected.
As we know, Python uses reference counting mechanism for garbage collection and reference count is increased when there is a strong reference. Let’s see an example:
On running the file python weak-ref-eg-0.py
, the output is:
Reference count for yobj: 1
Reference count of w_yobj is 1
Reference count for yobj: 2
We see that the reference count is increased only when there is strong reference as in line number 21
.
We can extend more on this program to delete the objects with strong reference and see the decrease in the reference count. After the cleanup of the objects if we call the weakly referenced object w_yobj
it will return None
.
Output is:
Reference count for yobj: 1
Reference count of w_yobj: 1
Reference count for yobj: 2Cleaning...After deleting yobj_2, reference count for yobj: 1calling weakref object, w_yobj, after deleting both objects...
Weak ref: None
Why is weakref used for? Very good question. It is used for
- In caching or mapping where the objects you are holding are expensive and you don’t want them to be alive just because it is in the mapping or caching.
2. To make life easier to deal with circular references.
Let’s see an example for mapping:
On running the file, the output is:
cache type: <class 'dict'>
reference count after initialization gem opal: 1
reference count after caching gem opal: 2
reference count after appending to list gem opal: 3reference count after initialization gem ruby: 1
reference count after caching gem ruby: 2
reference count after appending to list gem ruby: 3reference count after initialization gem fluorite: 1
reference count after caching gem fluorite: 2
reference count after appending to list gem fluorite: 3List length: 3, contents: [PriceyObject(opal), PriceyObject(ruby), PriceyObject(fluorite)]cleaning list...After cleaning list, cache contains: dict_keys(['opal', 'ruby', 'fluorite'])opal=PriceyObject(opal)
ruby=PriceyObject(ruby)
fluorite=PriceyObject(fluorite)demo returningDeleting PriceyObject(opal)
Deleting PriceyObject(ruby)
Deleting PriceyObject(fluorite)cache type: <class 'weakref.WeakValueDictionary'>
reference count after initialization gem opal: 1
reference count after caching gem opal: 1
reference count after appending to list gem opal: 2reference count after initialization gem ruby: 1
reference count after caching gem ruby: 1
reference count after appending to list gem ruby: 2reference count after initialization gem fluorite: 1
reference count after caching gem fluorite: 1
reference count after appending to list gem fluorite: 2List length: 3, contents: [PriceyObject(opal), PriceyObject(ruby), PriceyObject(fluorite)]cleaning list...Deleting PriceyObject(fluorite)
Deleting PriceyObject(ruby)
Deleting PriceyObject(opal)After cleaning, cache contains: <generator object WeakValueDictionary.keys at 0x7f8a700addd0>demo returning
The difference between standard dict
and weakref
module’s WeakValueDictionary
is that on deleting list containing strongly reference gem objects, WeakValueDictionary
does not contain the references to the gems object. While standard dictionary still has references and is able to iterate over them.
Now let’s come to an example of dealing with circular reference.
I am pulling an example from the memory management article which is about symmetric cousin relationship, here the cat’s genealogy.
The output is:
servy's refcount: 2
whiky's refcount: 2AFTER DELETION OF servy
whiky's refcount: 2AFTER ASSIGNING whiky's cousin to be None
whiky's refcount: 1
We saw circular reference which cause memory leaks by seeing reference count of whiky
object still being 2 after deletion of servy
object. We overcame this circular reference by manually setting whiky
‘s cousin to be None
.
We can deal with this type of relationship by using weak reference.
Output of the file is:
servy's refcount: 1
whiky's refcount: 1AFTER DELETION OF servywhiky's refcount: 1
whiky's cousin: None
We see that reference count does not increase by setting value of cousin
attribute and when servy
object is deleted whiky
's cousin
attribute’s value is None
.
Wasn’t that easy peasy?!
In conclusion, weakref
module provides ways to refer to objects without additional huge memory need.
Do explore the official documentation to see all methods.
Thank you for reading. I’ll see you soon. ✨