26. Methods and self
You have called methods on objects many times —
"hello".upper(), my_list.append(x),
math.floor(n). This chapter shows how to define your own
methods that belong to a particular object, and what
self is. Understanding self once explains a
lot of Python code.
Functions on objects, the long way
In Python, a method is just a function defined inside a class. But before classes, think about a plain dictionary holding a counter:
counter = {"count": 0}
def step(c):
c["count"] = c["count"] + 1
step(counter)
step(counter)
step(counter)
print(counter["count"]) # 3The function step takes the dictionary itself as its
first argument, then uses it to update the count. Every call has to pass
counter explicitly: step(counter). This works
but reads awkwardly.
Methods and self in a class
Python's class syntax solves this. A method is a function defined
inside a class body. When you call obj.method(), Python
automatically passes obj as the first argument. By
convention that first parameter is named self:
class Counter:
def __init__(self):
self.count = 0
def step(self):
self.count = self.count + 1
counter = Counter()
counter.step()
counter.step()
counter.step()
print(counter.count) # 3counter.step() is exactly the same as calling
Counter.step(counter) — Python fills in self
automatically from the object before the dot.
Inside the body, self is a normal parameter. Write
self.x to read a field, self.x = ... to change
one. The name self is a strong convention in Python; every
method should use it.
Open exercises/26/01-counter.py. It already has
Counter with a step method. Add a
reset method that sets self.count back to
0, and call it from the bottom of the file.
Built-in method calls
Python's built-in types use the same mechanism. A string is an
object; calling s.upper() passes s as
self to the string's upper method:
name = "keiko"
print(name.upper()) # KEIKO
print(len(name)) # 5Every method you call on a list, string, or dictionary works this way. Your own classes behave identically.
A small game object
Method syntax shines when several pieces of state go together. Here is a dog with a name and a number of barks left:
class Dog:
def __init__(self, name, barks_left):
self.name = name
self.barks_left = barks_left
def bark(self):
if self.barks_left <= 0:
print(f"{self.name} is hoarse.")
return
print(f"{self.name}: Woof!")
self.barks_left = self.barks_left - 1
dog = Dog("Rex", 3)
dog.bark() # Rex: Woof!
dog.bark() # Rex: Woof!
dog.bark() # Rex: Woof!
dog.bark() # Rex is hoarse.The Dog object holds the state; the method changes it on
every call. This is the core of object orientation: an object with data
and the methods that act on it.
Common mistake: forgetting self in the definition
If you forget self as the first parameter, Python still
passes the object, but your method receives it in the wrong slot:
class Counter:
def __init__(self):
self.count = 0
def step(): # missing self
self.count += 1
c = Counter()
c.step() # TypeError: step() takes 0 positional arguments but 1 was givenThe fix is always to add self as the first parameter of
every method.
Homework
Problem 1 — Counter object
Open exercises/26/homework/01-counter-object.py. Build a
Counter class with the fields and methods:
self.count— starts at0.inc()— adds1.get()— returns the current count.reset()— setscountback to0.
Call them in sequence and print the value at each step.
Problem 2 — Dog with bark
Open exercises/26/homework/02-dog.py. Build a
Dog class with a name field and a
bark() method that prints <name>: Woof!.
Make a second dog with a different name. Call both bark()
and confirm each prints its own name.
Problem 3 — Calculator object
Open exercises/26/homework/03-calculator.py. Build a
Calc class with:
self.value— starts at0.add(n)— addsntoself.value.sub(n)— subtracts.mul(n)— multiplies.show()— prints the current value with a label.
Chain a few calls and confirm the value updates as expected.
Challenge — Stack
Open exercises/26/homework/04-stack.py. Build a
Stack class that behaves like a stack:
self.items— a list.push(v)— addsvto the top.pop()— removes and returns the top.peek()— returns the top without removing it (orNoneif empty).size()— returns how many items are stored.
Use self.items.append(v) and
self.items.pop() without an index — that pushes and pops at
the end of the list, the standard stack behaviour.
Stuck or finished? Open the homework solutions page.