Skip to content

Exercises - __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 cost
print(sys.getsizeof(r)) # ?
print(sys.getsizeof(s)) # ?
# Object 2 cost
print(sys.getsizeof(r.__dict__)) # ?
print(sys.getsizeof(s.__dict__)) # ? — what happens here?
# true total
regular_total = sys.getsizeof(r) + sys.getsizeof(r.__dict__)
print(f"regular total: {regular_total} bytes") # ?
print(f"slotted total: {sys.getsizeof(s)} bytes") # ?

Exercise 4🟢 Beginner Add __slots__ to this class and verify it works correctly:

# ❌ regular class — add __slots__ to save memory
class 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" → ❌ AttributeError

Exercise 5🟢 Beginner Fix this broken slotted class — it is missing a slot:

# ❌ broken — missing a slot
class 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 → ❌ AttributeError

Exercise 7🟡 Intermediate Measure the memory difference between regular and slotted versions of this class at scale:

import sys
import 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?

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) # 8080
print(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" → ❌ AttributeError

Exercise 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 class

Try 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.