Exercises - __slots__
Exercises — __slots__
Section titled “Exercises — __slots__”Understanding __dict__ vs __slots__
Section titled “Understanding __dict__ vs __slots__”Exercise 1 — 🟢 Beginner
Predict the output of the following code before running it:
import sys
class RegularPoint: def __init__(self, x, y): self.x = x self.y = y
rp = RegularPoint(1.0, 2.0)
print(type(rp.__dict__)) # ?print(rp.__dict__) # ?print(sys.getsizeof(rp)) # ?print(sys.getsizeof(rp.__dict__)) # ?
rp.color = "red" # can we add a new attribute?print(rp.__dict__) # ?Exercise 2 — 🟢 Beginner
Predict the output and explain what happens when you try to access __dict__ on a slotted instance:
import sys
class SlottedPoint: __slots__ = ["x", "y"]
def __init__(self, x, y): self.x = x self.y = y
sp = SlottedPoint(1.0, 2.0)
print(sys.getsizeof(sp)) # ?print(sp.x) # ?print(sp.y) # ?print(sp.__dict__) # ? — what happens here?sp.color = "red" # ? — what happens here?Exercise 3 — 🟡 Intermediate
Predict the output and explain the memory difference between the two classes:
import sys
class Regular: def __init__(self, name, age, email): self.name = name self.age = age self.email = email
class Slotted: __slots__ = ["name", "age", "email"]
def __init__(self, name, age, email): self.name = name self.age = age self.email = email
r = Regular("Alice", 30, "alice@example.com")s = Slotted("Alice", 30, "alice@example.com")
# Object 1 costprint(sys.getsizeof(r)) # ?print(sys.getsizeof(s)) # ?
# Object 2 costprint(sys.getsizeof(r.__dict__)) # ?print(sys.getsizeof(s.__dict__)) # ? — what happens here?
# true totalregular_total = sys.getsizeof(r) + sys.getsizeof(r.__dict__)print(f"regular total: {regular_total} bytes") # ?print(f"slotted total: {sys.getsizeof(s)} bytes") # ?Defining __slots__
Section titled “Defining __slots__”Exercise 4 — 🟢 Beginner
Add __slots__ to this class and verify it works correctly:
# ❌ regular class — add __slots__ to save memoryclass Vector2D: def __init__(self, x, y): self.x = x self.y = y
def magnitude(self): return (self.x ** 2 + self.y ** 2) ** 0.5
v = Vector2D(3.0, 4.0)
# after adding __slots__:# v.magnitude() → 5.0# v.__dict__ → ❌ AttributeError# v.color = "red" → ❌ AttributeErrorExercise 5 — 🟢 Beginner
Fix this broken slotted class — it is missing a slot:
# ❌ broken — missing a slotclass Circle: __slots__ = ["x", "y"] # missing something!
def __init__(self, x, y, radius): self.x = x self.y = y self.radius = radius # ❌ AttributeError — why?
def area(self): import math return math.pi * self.radius ** 2
c = Circle(0, 0, 5)print(c.area()) # should print 78.539...Exercise 6 — 🟡 Intermediate
Add __slots__ to this class that represents a pixel in an image. Explain why this is a good candidate for __slots__:
# this class will be instantiated millions of times# for a 4K image (3840 × 2160 = 8,294,400 pixels)
class Pixel: def __init__(self, r, g, b, alpha=255): self.r = r self.g = g self.b = b self.alpha = alpha
def is_transparent(self): return self.alpha == 0
def to_hex(self): return f"#{self.r:02x}{self.g:02x}{self.b:02x}"
# after adding __slots__:# p = Pixel(255, 128, 0)# p.to_hex() → "#ff8000"# p.is_transparent() → False# p.brightness = 100 → ❌ AttributeErrorMemory Comparison
Section titled “Memory Comparison”Exercise 7 — 🟡 Intermediate
Measure the memory difference between regular and slotted versions of this class at scale:
import sysimport tracemalloc
class RegularUser: def __init__(self, user_id, name, email, active): self.user_id = user_id self.name = name self.email = email self.active = active
class SlottedUser: # add __slots__ here def __init__(self, user_id, name, email, active): self.user_id = user_id self.name = name self.email = email self.active = active
# measure memory for 100,000 instances of each# expected:# regular: ~XX MB# slotted: ~XX MB ← significantly less# saving: ~XX%Exercise 8 — 🟡 Intermediate
Compare the memory cost per instance between these two classes and explain the difference:
import sys
class SmallRegular: def __init__(self, x): self.x = x
class SmallSlotted: __slots__ = ["x"]
def __init__(self, x): self.x = x
r = SmallRegular(42)s = SmallSlotted(42)
# Questions:# 1. What is the total memory cost of r (object + __dict__)?# 2. What is the total memory cost of s?# 3. What is the percentage saving?# 4. Is the saving proportionally bigger or smaller# compared to a class with 3 attributes?# why?Trade-offs
Section titled “Trade-offs”Exercise 9 — 🟡 Intermediate
This code relies on dynamic attribute assignment. Explain why converting it to use __slots__ would break it, and propose a solution:
class FlexibleConfig: def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) # dynamic attribute assignment
cfg = FlexibleConfig(host="localhost", port=8080, debug=True)print(cfg.host) # "localhost"print(cfg.port) # 8080print(cfg.debug) # True
# Questions:# 1. Why would adding __slots__ = [] break this?# 2. Can __slots__ and dynamic attributes coexist?# hint: add "__dict__" to __slots__# 3. What is the memory implication of that solution?Exercise 10 — 🔴 Advanced
This code uses inheritance. Predict what happens with __slots__ across the inheritance chain and fix the memory leak:
import sys
class Base: __slots__ = ["x", "y"]
def __init__(self, x, y): self.x = x self.y = y
class Child(Base): # missing __slots__ — what happens? def __init__(self, x, y, z): super().__init__(x, y) self.z = z
c = Child(1, 2, 3)
print(sys.getsizeof(c)) # ?print(hasattr(c, "__dict__")) # ? — does Child have a __dict__?c.color = "red" # ? — does this work?
# Questions:# 1. Why does Child have a __dict__ even though Base uses __slots__?# 2. Fix Child so it also uses __slots__ correctly# 3. After the fix, verify:# hasattr(c, "__dict__") → False# c.color = "red" → ❌ AttributeErrorExercise 11 — 🔴 Advanced
Design a memory-efficient class for storing GPS coordinates that will be used in a mapping application processing millions of location points:
# Requirements:# - stores latitude, longitude, altitude, and timestamp# - must be as memory-efficient as possible# - must support comparison by latitude# - must support __repr__ for debugging# - must NOT allow dynamic attributes# - will be instantiated millions of times
# expected:# p = GPSPoint(51.5074, -0.1278, 11.0, 1234567890)# p.latitude → 51.5074# p.longitude → -0.1278# p.altitude → 11.0# p.timestamp → 1234567890# p.unknown = "x" → ❌ AttributeError# repr(p) → "GPSPoint(51.5074, -0.1278, 11.0, 1234567890)"
# measure and report the memory saving vs a regular classTry measuring the actual memory difference using sys.getsizeof() and tracemalloc for every exercise — the goal is to build an intuition for when __slots__ is worth the trade-off in flexibility.