Exercises - Memory model
Exercises — Python Memory Model
Section titled “Exercises — Python Memory Model”References and Identity
Section titled “References and Identity”Exercise 1 — 🟢 Beginner
Predict the output of the following code before running it, then verify your answer.
a = [1, 2, 3]b = ac = [1, 2, 3]
print(a == b) # ?print(a is b) # ?print(a == c) # ?print(a is c) # ?
b.append(4)print(a) # ?print(c) # ?Exercise 2 — 🟢 Beginner
Predict the output of the following code before running it, then verify your answer.
a = 42b = a
print(a is b) # ?
a += 1
print(a) # ?print(b) # ?print(a is b) # ?Exercise 3 — 🟡 Intermediate
Predict the output of the following code before running it. Pay close attention to what is returns for integers inside and outside the CPython cache range.
a = 256b = 256print(a is b) # ?
a = 257b = 257print(a is b) # ?
a = -5b = -5print(a is b) # ?
a = -6b = -6print(a is b) # ?Shallow vs Deep Copy
Section titled “Shallow vs Deep Copy”Exercise 4 — 🟢 Beginner
Predict the output of the following code before running it.
import copy
a = [1, 2, 3]b = a.copy()
a.append(4)
print(a) # ?print(b) # ?print(a is b) # ?Exercise 5 — 🟡 Intermediate
Predict the output of the following code before running it. Think carefully about what shallow copy means for nested structures.
import copy
original = [[1, 2], [3, 4]]shallow = original.copy()deep = copy.deepcopy(original)
original[0].append(99)original.append([5, 6])
print(original) # ?print(shallow) # ?print(deep) # ?Exercise 6 — 🟡 Intermediate
Fix this function so that modifying the returned list does not affect the original:
# ❌ broken — modifying result affects originaldef get_items(cart): return cart
my_cart = ["apple", "banana", "cherry"]result = get_items(my_cart)result.append("date")
print(my_cart) # should be ["apple", "banana", "cherry"] # but currently shows ["apple", "banana", "cherry", "date"]Exercise 7 — 🔴 Advanced
Predict the output of the following code, then explain why shallow behaves the way it does.
import copy
original = {"a": [1, 2, 3], "b": [4, 5, 6]}shallow = original.copy()deep = copy.deepcopy(original)
original["a"].append(99)original["c"] = [7, 8, 9]
print(original) # ?print(shallow) # ?print(deep) # ?Never Mutate — Functional Approach
Section titled “Never Mutate — Functional Approach”Exercise 8 — 🟢 Beginner
Rewrite this mutating function so it returns a new list instead of modifying the original:
# ❌ mutatingdef double_all(numbers): for i in range(len(numbers)): numbers[i] *= 2 return numbers
nums = [1, 2, 3, 4, 5]result = double_all(nums)
# after fix:# nums should still be [1, 2, 3, 4, 5]# result should be [2, 4, 6, 8, 10]Exercise 9 — 🟢 Beginner
Rewrite this mutating function so it returns a new dict instead of modifying the original:
# ❌ mutatingdef activate_user(user): user["active"] = True return user
u = {"name": "Alice", "active": False}result = activate_user(u)
# after fix:# u should still be {"name": "Alice", "active": False}# result should be {"name": "Alice", "active": True}Exercise 10 — 🟡 Intermediate
Rewrite this mutating function that removes all negative numbers from a list, without modifying the original:
# ❌ mutatingdef remove_negatives(numbers): for n in numbers[:]: if n < 0: numbers.remove(n) return numbers
nums = [3, -1, 4, -2, 5, -3, 6]result = remove_negatives(nums)
# after fix:# nums should still be [3, -1, 4, -2, 5, -3, 6]# result should be [3, 4, 5, 6]Exercise 11 — 🟡 Intermediate
Rewrite this mutating function that applies a discount to all items in a shopping cart, without modifying the original:
# ❌ mutatingdef apply_discount(cart, discount): for item in cart: item["price"] *= (1 - discount) return cart
cart = [ {"name": "apple", "price": 1.0}, {"name": "banana", "price": 0.5}, {"name": "cherry", "price": 3.0},]result = apply_discount(cart, 0.10)
# after fix:# cart should still have original prices# result should have discounted prices:# [{"name": "apple", "price": 0.9},# {"name": "banana", "price": 0.45},# {"name": "cherry", "price": 2.7}]Exercise 12 — 🔴 Advanced
Rewrite this mutating function that updates a nested configuration dict, without modifying the original at any level:
# ❌ mutatingdef update_config(config, section, key, value): config[section][key] = value return config
config = { "server": {"host": "localhost", "port": 8080}, "database": {"host": "localhost", "port": 5432},}result = update_config(config, "server", "port", 3000)
# after fix:# config["server"]["port"] should still be 8080# result["server"]["port"] should be 3000# config and result should be different objects# config["database"] should be the same object in both# (only the modified section needs a new object)Try solving each exercise without using copy.deepcopy where possible — the goal is to build the habit of constructing new objects naturally rather than copying and mutating.