30. Designing a small class — Homework solutions

The .py solution files are in exercises/30/homework/solutions/.

Problem 1 — Counter class

Worked solution.

class Counter:
    def __init__(self):
        self.count = 0

    def increment(self):
        self.count = self.count + 1

    def get(self):
        return self.count

    def reset(self):
        self.count = 0

c = Counter()
c.increment()
c.increment()
c.increment()
print(c.get())   # 3
c.reset()
print(c.get())   # 0

Problem 2 — Clamped health

How to think about it. Enforce "stay between 0 and max" inside heal and damage, so no caller can push health out of range.

Worked solution.

class Health:
    def __init__(self, max):
        self.max = max
        self.current = max

    def heal(self, n):
        self.current = self.current + n
        if self.current > self.max:
            self.current = self.max

    def damage(self, n):
        self.current = self.current - n
        if self.current < 0:
            self.current = 0

    def get(self):
        return self.current

h = Health(100)
h.damage(30)
print(h.get())    # 70
h.heal(1000)
print(h.get())    # 100  (capped at max)
h.damage(9999)
print(h.get())    # 0    (floored at 0)

Problem 3 — Light switch

Worked solution.

class Switch:
    def __init__(self):
        self.on = False

    def toggle(self):
        self.on = not self.on

    def is_on(self):
        return self.on

s = Switch()
print(s.is_on())    # False
s.toggle()
print(s.is_on())    # True
s.toggle()
print(s.is_on())    # False

self.on = not self.on flips the boolean each call — the whole switch in one line.

Challenge — Stack with a guard

Problem. pop() on an empty stack returns None instead of crashing.

Worked solution.

class Stack:
    def __init__(self):
        self.items = []

    def push(self, v):
        self.items.append(v)

    def pop(self):
        if len(self.items) == 0:
            return None
        return self.items.pop()

    def size(self):
        return len(self.items)

s = Stack()
s.push("a")
s.push("b")
print(s.size())   # 2
print(s.pop())    # b
print(s.pop())    # a
print(s.pop())    # None   (empty, but no crash)
print(s.size())   # 0

list.pop() on an empty list raises an IndexError, so the guard checks length first. That is the rule the method protects.

Done?

That is the end of Part 6. You can build classes, make many instances, inherit, override, and design a clean, safe public surface. Part 6 has two mini-projects: the Inventory System and a Monster Battle, both built from cooperating objects.