Classes and objectsChapter 62 of 114
Methods
Functions that belong to a class, and the three kinds of them.
A method is a function on a class
class Dog:
def __init__(self, name):
self.name = name
self.tricks = []
def learn(self, trick):
self.tricks.append(trick)
def show(self):
return f"{self.name} knows: {', '.join(self.tricks)}"
rex = Dog("Rex")
rex.learn("sit")
rex.learn("roll")
print(rex.show())Output
Rex knows: sit, roll
rex.learn("sit") passes rex as self automatically. That is the whole trick of method calls.
Methods can call each other
Through self, a method can use anything else on the object:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def describe(self):
return f"{self.width}x{self.height}, area {self.area()}"
print(Rectangle(3, 4).describe())Output
3x4, area 12
Forgetting self. is the common slip. A bare area() looks for a plain function of that name and raises NameError.
Returning versus changing
A method can change the object, return a value, or both. Be clear which:
class Counter:
def __init__(self):
self.value = 0
def bump(self):
self.value += 1
def doubled(self):
return self.value * 2
c = Counter()
c.bump()
print(c.value, c.doubled())Output
1 2
Returning self lets calls chain, which some libraries use heavily:
class Query:
def __init__(self):
self.parts = []
def where(self, clause):
self.parts.append(clause)
return self
def build(self):
return " AND ".join(self.parts)
print(Query().where("a = 1").where("b = 2").build())Output
a = 1 AND b = 2
classmethod
Takes the class rather than an instance. The main use is an alternative way to build one:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def puppy(cls, name):
return cls(name, 0)
rex = Dog.puppy("Rex")
print(rex.name, rex.age)Output
Rex 0
cls is the class, so cls(name, 0) builds one. Using cls rather than Dog means a subclass gets its own type back.
staticmethod
Takes neither. It is a plain function that lives in the class because it belongs there conceptually:
class Temperature:
@staticmethod
def c_to_f(celsius):
return celsius * 9 / 5 + 32
print(Temperature.c_to_f(100))Output
212.0
If it never touches self or cls, it can be a staticmethod — or, just as often, a module-level function.
property
Makes a method look like an attribute. Use it for a value derived from others:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
r = Rectangle(3, 4)
print(r.area)
r.width = 10
print(r.area)Output
12 40
No brackets on r.area, and it recalculates. Storing self.area in __init__ instead would have gone stale the moment width changed.
A setter adds validation without changing how callers write it:
class Dog:
def __init__(self, age):
self._age = age
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("age cannot be negative")
self._age = value
rex = Dog(3)
rex.age = 4
print(rex.age)
try:
rex.age = -1
except ValueError as problem:
print("ValueError:", problem)Output
4 ValueError: age cannot be negative
Test yourself
2 questionsWhat does @property let you do?
Show the answer
Call a method without brackets, like an attribute — It is how a derived value stays correct when the values it depends on change.
What does a classmethod receive as its first argument?
Show the answer
The class — Using cls(...) rather than the class name by hand means a subclass gets its own type back.
self
What self actually is, and why you have to write it.