Classes and objectsChapter 63 of 114
self
What self actually is, and why you have to write it.
self is the object
When you call rex.learn("sit"), Python turns it into Dog.learn(rex, "sit"). The first parameter receives the object the method was called on:
class Dog:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} says woof"
rex = Dog("Rex")
print(rex.speak())
print(Dog.speak(rex))Output
Rex says woof Rex says woof
Both lines do the same thing. The dotted form is sugar for the second.
It is a name, not a keyword
self is a convention, not a rule. This works:
class Dog:
def __init__(banana, name):
banana.name = name
def speak(banana):
return f"{banana.name} says woof"
print(Dog("Rex").speak())Output
Rex says woof
Never do this. Every Python reader expects self, and using anything else costs them a double-take for no gain.
Forgetting it
Leave self out of the definition and the call fails, because Python still passes the object:
class Dog:
def speak():
return "woof"
try:
Dog().speak()
except TypeError as problem:
print("TypeError:", problem)Output
TypeError: Dog.speak() takes 0 positional arguments but 1 was given
The error message is the clue: one argument was given, invisibly.
Attributes need self too
Inside a method, a bare name is a local variable. To reach the object's data you have to go through self:
class Counter:
def __init__(self):
self.value = 0
def broken(self):
value = 99 # a local, thrown away
def working(self):
self.value = 99 # the object's attribute
c = Counter()
c.broken()
print(c.value)
c.working()
print(c.value)Output
0 99
This is why Python makes you write it. There is no ambiguity about whether a name is local or an attribute, because the two look different.
self in one method, visible in another
Anything you attach in __init__ is available everywhere else on the object:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = []
def learn(self, trick):
self.tricks.append(trick)
def count(self):
return len(self.tricks)
rex = Dog("Rex")
rex.learn("sit")
print(rex.count())Output
1
Bound methods
rex.speak without brackets is a bound method: the function with rex already attached. You can store and pass it around:
class Dog:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} woofs"
rex = Dog("Rex")
talk = rex.speak
print(talk())
print([d.speak() for d in [Dog("A"), Dog("B")]])Output
Rex woofs ['A woofs', 'B woofs']
self is not special inside
It is an ordinary parameter holding an ordinary object, so you can compare it, return it, or pass it on:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def older_than(self, other):
return self.age > other.age
rex = Dog("Rex", 5)
fido = Dog("Fido", 3)
print(rex.older_than(fido))Output
True
Test yourself
2 questionsWhat is self?
Show the answer
The object the method was called on — rex.speak() is sugar for Dog.speak(rex). The name is only a convention.
Inside a method, what does a bare 'value = 99' do?
Show the answer
Creates a local variable that is thrown away — Attributes need self. That explicitness is exactly why Python makes you write it.
Inheritance
Build a class on top of another, and override what differs.