Exercises - Reference counting
Exercises — Reference Counting
Section titled “Exercises — Reference Counting”Basic Reference Counting
Section titled “Basic Reference Counting”Exercise 1 — 🟢 Beginner
Predict the reference count at each step before running the code:
import sys
a = [1, 2, 3]print(sys.getrefcount(a)) # ?
b = aprint(sys.getrefcount(a)) # ?
c = aprint(sys.getrefcount(a)) # ?
del bprint(sys.getrefcount(a)) # ?
del cprint(sys.getrefcount(a)) # ?Exercise 2 — 🟢 Beginner
Predict the reference count at each step. Think carefully about what happens when a variable is reassigned:
import sys
a = [1, 2, 3]b = aprint(sys.getrefcount(a)) # ?
b = [4, 5, 6] # b is reassigned to a new objectprint(sys.getrefcount(a)) # ?print(sys.getrefcount(b)) # ?Exercise 3 — 🟡 Intermediate
Predict the reference count at each step. Think carefully about what happens when an object is stored inside a list:
import sys
a = [1, 2, 3]print(sys.getrefcount(a)) # ?
container = [a, a, a] # a is stored three timesprint(sys.getrefcount(a)) # ?
container.pop() # remove one reference from containerprint(sys.getrefcount(a)) # ?
del container # remove all remaining referencesprint(sys.getrefcount(a)) # ?References in Data Structures
Section titled “References in Data Structures”Exercise 4 — 🟡 Intermediate
Predict the reference count at each step. Think about how dict values affect reference counts:
import sys
a = {"name": "Alice"}print(sys.getrefcount(a)) # ?
d = {"user": a} # a stored as dict valueprint(sys.getrefcount(a)) # ?
d["admin"] = a # a stored again under different keyprint(sys.getrefcount(a)) # ?
del d # entire dict removedprint(sys.getrefcount(a)) # ?Exercise 5 — 🟡 Intermediate
Predict the reference count at each step. Think about what happens when a function receives an argument:
import sys
def inspect(obj): print(sys.getrefcount(obj)) # ? — what is the count inside the function?
a = [1, 2, 3]print(sys.getrefcount(a)) # ? — before the call
inspect(a) # what happens to refcount inside?
print(sys.getrefcount(a)) # ? — after the call, back to what?Circular References
Section titled “Circular References”Exercise 6 — 🟡 Intermediate
Predict what happens to the reference counts after the del statements. Draw a diagram showing why neither object is freed:
import sys
a = {}b = {}
a["ref"] = bb["ref"] = a
print(sys.getrefcount(a)) # ?print(sys.getrefcount(b)) # ?
del adel b
# Are the objects freed? Why or why not?# What tool does Python use to handle this situation?Exercise 7 — 🟡 Intermediate
This code creates a circular reference inside a list. Predict the reference counts and explain why the objects are not freed after del:
import sys
a = [1, 2, 3]b = [4, 5, 6]
a.append(b) # a holds a reference to bb.append(a) # b holds a reference to a
print(sys.getrefcount(a)) # ?print(sys.getrefcount(b)) # ?
del adel b
# Draw the diagram showing the state of the heap after del# Why is the cyclic garbage collector needed here?Exercise 8 — 🔴 Advanced
This code creates a three-way circular reference. Predict the reference counts at each step and draw the heap diagram after all del statements:
import sys
a = {}b = {}c = {}
a["next"] = bb["next"] = cc["next"] = a # closes the cycle
print(sys.getrefcount(a)) # ?print(sys.getrefcount(b)) # ?print(sys.getrefcount(c)) # ?
del adel bdel c
# Draw the heap diagram showing:# 1. the three objects and their internal references# 2. why all three refcounts remain at 1# 3. why none of them are freed by reference counting aloneSpot the Memory Issue
Section titled “Spot the Memory Issue”Exercise 9 — 🔴 Advanced
Identify the memory issue in this code and explain why it is a problem:
class Node: def __init__(self, value): self.value = value self.next = None
# Building a linked listn1 = Node(1)n2 = Node(2)n3 = Node(3)
n1.next = n2n2.next = n3n3.next = n1 # ← what is wrong here?
del n1del n2del n3
# Questions:# 1. What are the reference counts of n1, n2, n3 after del?# 2. Are the objects freed?# 3. How would you fix this?Exercise 10 — 🔴 Advanced
This code builds a cache that unintentionally prevents objects from being freed. Identify the issue, explain it in terms of reference counting, and propose a fix using weakref:
import sys
cache = {}
class ExpensiveObject: def __init__(self, name): self.name = name
obj = ExpensiveObject("resource")cache["key"] = obj # stored in cache
print(sys.getrefcount(obj)) # ?
del obj # we are done with obj
# Questions:# 1. Is the object freed after del obj? Why or why not?# 2. What is the reference count after del obj?# 3. How would weakref solve this problem?# hint: import weakref; cache["key"] = weakref.ref(obj)Try predicting every reference count before running the code — the goal is to build an accurate mental model of how Python tracks object lifetimes, so memory issues become visible before they become bugs.