27. Simple OOP with classes
Chapter 26 put a few methods on one object created by hand. That
works for one object, but wastes effort for many
objects of the same shape. Imagine 100 dogs — copying bark
onto each one separately is impractical. This chapter shows the Python
class as a blueprint: you define it once, then call it
like a function to create as many instances as you want.
A class with
__init__ and methods
Here is a Point class. Read it, then check the
explanation:
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, other):
dx = self.x - other.x
dy = self.y - other.y
return math.sqrt(dx * dx + dy * dy)
a = Point(0, 0)
b = Point(3, 4)
print(a.distance(b)) # 5.0What is going on:
class Point:declares the class — a blueprint. All methods are indented inside it.__init__(self, x, y)is the constructor. Python calls it automatically whenever you writePoint(0, 0). It sets up the instance's fields.selfis the instance being created or used. Writingself.x = xstoresxon the instance itself.Point(0, 0)creates a new instance — a separate object with its ownxandy.def distance(self, other)is a regular method.selfreceives the instance the method was called on;otheris the argument you passed in.a.distance(b)calls the method. Python fills inself = aautomatically; you only passother = b.
The result: one class definition, many small instances, no copying.
Open exercises/27/01-point.py. Add a
move(self, dx, dy) method that adds dx to
self.x and dy to self.y. Create a
point, move it twice, then print its position.
Why instances and not just dictionaries?
A plain dictionary can hold the same data, but a class groups the
data and the behaviour together in one place. The class also
acts as a factory: each call to Point(x, y) produces a
fresh, independent object. Two instances never share fields unless you
explicitly make them do so.
A second class: Character
A small character class that can take damage:
class Character:
def __init__(self, name, hp):
self.name = name
self.hp = hp
self.max_hp = hp
def take_damage(self, amount):
self.hp = self.hp - amount
if self.hp < 0:
self.hp = 0
def heal(self, amount):
self.hp = self.hp + amount
if self.hp > self.max_hp:
self.hp = self.max_hp
def is_alive(self):
return self.hp > 0
c = Character("Keiko", 100)
c.take_damage(30)
print(c.hp) # 70
c.heal(50)
print(c.hp) # 100 (capped at max_hp)
print(c.is_alive()) # TrueEach Character(name, hp) call produces a fresh instance
with its own name, hp, and
max_hp. All the methods live inside the class and are
shared by every instance.
Inheritance: one class building on another
A child class inherits all the methods of a parent by naming it in parentheses:
class Animal:
def __init__(self, name):
self.name = name
def describe(self):
print(f"I am {self.name}.")
class Dog(Animal):
def __init__(self, name):
super().__init__(name) # run Animal's __init__
def bark(self):
print(f"{self.name}: Woof!")
rex = Dog("Rex")
rex.describe() # I am Rex. (inherited from Animal)
rex.bark() # Rex: Woof! (defined on Dog)class Dog(Animal): says Dog inherits from Animal.
super().__init__(name) calls Animal's constructor so the
name field is set up correctly.
rex.describe() works even though Dog does
not define it — Python finds it on Animal
automatically.
That is enough inheritance for most purposes. The next chapters go deeper into overriding and designing a class well.
Homework
Problem 1 — Point class with move
Open exercises/27/homework/01-point.py. Build a
Point class as above, plus a move(dx, dy)
method that adds the deltas to the position. Test by creating two
points, moving one, then printing the distance between them.
Problem 2 — Character class
Open exercises/27/homework/02-character.py. Build the
Character class with __init__(name, hp),
take_damage(amount), heal(amount), and
is_alive(). Add a report() method that prints
the name and current HP. Walk through a small fight: damage twice, heal
once, and print the report after each action.
Problem 3 — Rectangle class
Open exercises/27/homework/03-rectangle.py. Build a
Rectangle class with __init__(width, height),
area(), and perimeter(). Test it with two
rectangles of different sizes.
Challenge — Animal and Dog
Open exercises/27/homework/04-inheritance.py. Build
Animal and Dog exactly as in the chapter's
inheritance example. Then add a second child class Cat that
also inherits from Animal and has its own
meow() method. Create one dog and one cat, call
describe() on both, then bark() on the dog and
meow() on the cat.
Stuck or finished? Open the homework solutions page.