Python Arena
Step 2 of 4

Overriding methods

A derived class can also change a method it inherits. Write a method with the same name in the derived class, and objects of that class use the new version instead. This is called overriding the method.

Say knights wear armor that stops half of every hit:

class Character:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def take_damage(self, amount):
        self.health -= amount

    def __str__(self):
        return f"{self.name} ({self.health} health)"


class Knight(Character):
    def take_damage(self, amount):
        # The armor stops half of every hit.
        self.health -= amount // 2


bram = Knight("Bram", 50)
pip = Character("Pip", 50)
bram.take_damage(20)
pip.take_damage(20)
print(bram)
print(pip)
Output
Bram (40 health)
Pip (30 health)

When you call a method, Python first looks for it in the object's own class, and only if it isn't there, in the base class. bram is a Knight, and Knight has its own take_damage, so that's the one that runs. pip is a plain Character, so the original runs. __str__ isn't overridden, so both use the one in Character. It's a method like any other, though: a derived class can override __str__ too.

The base class calls the new version

Overriding reaches further than the calls you write yourself. When a method of the base class calls self.something(), and the object belongs to a derived class that overrides something, the overriding version runs:

class Greeter:
    def greeting(self, name):
        return f"Hello, {name}."

    def greet_all(self, names):
        for name in names:
            print(self.greeting(name))


class PirateGreeter(Greeter):
    def greeting(self, name):
        return f"Ahoy, {name}!"


friendly = Greeter()
pirate = PirateGreeter()
friendly.greet_all(["Ada", "Linus"])
pirate.greet_all(["Ada", "Linus"])
Output
Hello, Ada.
Hello, Linus.
Ahoy, Ada!
Ahoy, Linus!

greet_all is written only once, in Greeter. When it runs for pirate, self is the pirate greeter, so self.greeting(name) finds the pirate's version. This is a handy way to design classes: the base class does the general work and leaves one small method for each derived class to do in its own way.

Get the name exactly right

An overriding method must have exactly the same name as the method it replaces. If you misspell it, Python doesn't complain: it just adds a new method that nobody calls, and the old one keeps running. When an override seems to be ignored, check the spelling first.

Your task

Challenge 1 of 3

What does this program print? Type each line it prints.

Challenge 2

The class Ticket works out a ticket's price with the method final_price, and its __str__ uses that method. Write a class StudentTicket that inherits Ticket and overrides final_price: a student ticket costs 5 euros less than the normal price, but never less than 0. For example,

concert = StudentTicket("Concert", 25)
museum = StudentTicket("Museum", 4)
print(concert)
print(museum)
print(concert.final_price())

should print:

Concert: 20 euros
Museum: 0 euros
20

Challenge 3

The class WordFilter goes through a list of words in filter and keeps the ones that its method keeps says yes to. On its own it keeps every word. Write two classes that inherit WordFilter and override only keeps:

  • ShortWords keeps the words of at most 4 letters.
  • LongWords keeps the words of at least 8 letters.

For example,

words = ["cat", "elephant", "dog", "giraffe", "hippopotamus"]
short = ShortWords()
long = LongWords()
print(short.filter(words))
print(long.filter(words))

should print:

['cat', 'dog']
['elephant', 'hippopotamus']