26. Methods and self — Homework solutions
The .py solution files are in
exercises/26/homework/solutions/.
Problem 1 — Counter object
Problem. A Counter class with
inc, get, and reset methods.
How to think about it. The object holds the state in
self.count; each method reads or changes it. Python passes
the object as self automatically when you call the method
on an instance.
Worked solution.
class Counter:
def __init__(self):
self.count = 0
def inc(self):
self.count = self.count + 1
def get(self):
return self.count
def reset(self):
self.count = 0
counter = Counter()
counter.inc()
counter.inc()
counter.inc()
print(counter.get()) # 3
counter.reset()
print(counter.get()) # 0Common mistakes.
- Forgetting
selfas the first parameter of each method. Python will raise aTypeErrorsaying the method takes 0 arguments but 1 was given. - Writing
count = 0at class level instead ofself.count = 0in__init__. A class-level variable is shared by all instances, not stored per object.
Problem 2 — Dog with bark
Problem. Two Dog objects with their own
names, each calling its own bark().
How to think about it. Each Dog(name)
call creates a separate object with its own self.name. The
same method code runs for both, but self refers to a
different object each time.
Worked solution.
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name}: Woof!")
rex = Dog("Rex")
lassie = Dog("Lassie")
rex.bark() # Rex: Woof!
lassie.bark() # Lassie: Woof!Common mistakes.
- Using a global
namevariable insidebarkinstead ofself.name. Both dogs would print the same name.
Problem 3 — Calculator object
Problem. A Calc object with arithmetic
methods that update an internal value.
Worked solution.
class Calc:
def __init__(self):
self.value = 0
def add(self, n):
self.value = self.value + n
def sub(self, n):
self.value = self.value - n
def mul(self, n):
self.value = self.value * n
def show(self):
print(f"Value: {self.value}")
calc = Calc()
calc.add(10)
calc.show() # Value: 10
calc.mul(3)
calc.show() # Value: 30
calc.sub(5)
calc.show() # Value: 25Common mistakes.
- Returning the new value from each method instead of updating
self.value. Both are fine, but the spec asks for in-place updates.
Challenge — Stack
Problem. A LIFO stack with push,
pop, peek, size.
Worked solution.
class Stack:
def __init__(self):
self.items = []
def push(self, v):
self.items.append(v)
def pop(self):
return self.items.pop()
def peek(self):
if len(self.items) == 0:
return None
return self.items[-1]
def size(self):
return len(self.items)
stack = Stack()
stack.push("a")
stack.push("b")
stack.push("c")
print(stack.size()) # 3
print(stack.peek()) # c
print(stack.pop()) # c
print(stack.pop()) # b
print(stack.size()) # 1list.append() and list.pop() without an
index work at the end of the list — the right place for stack
operations.
Common mistakes.
- Calling
self.items.pop(0)— that removes from the front, making it a queue, not a stack, and it costs more because every other item shifts. - Not guarding
peek— callingself.items[-1]on an empty list raises anIndexError. - Forgetting to return from
popandpeek. The default return isNone, which silently breaks the test.
Done?
The next chapter takes this pattern further. With a proper
class, you stop writing __init__
boilerplate for every object individually and share method definitions
across many instances that you create cleanly with a constructor
call.