← Back to course
Python 11-12 / Week 07 / Tuesday
2/6
Week 07 Β· The Fleet

Tuesday

Override
// Subclasses reuse and override; one call, many behaviors
⏱ about 30 min

Tuesday: Override

Comet watches Dot's battery number on the screen and frowns. "Dot is supposed to be light," she says. "It should sip the battery, not gulp it like Pip."

Her first idea is to open rover.py and change step_use() to return 2.

Wren holds up his clipboard. "What does the evidence say? Pip and Dot both ended at battery 90 after two steps. If you change rover.py, Pip changes too, and the Pip tests expect 95."

Nova projects ScoutRover beside Rover, with one empty line inside ScoutRover glowing softly. "How can I help?" she asks. "A subclass can write its own version of a method."

Comet looks at you. "Lead programmer, where should the new number live?"

Override a method

A subclass can override a method of its base class by writing a method with the same name. For a ScoutRover, Python finds the ScoutRover version first, so that is the one that runs.

Here is the surprising part. move() is written once, in Rover. It calls self.step_use(). A base class method that calls another method may end up calling the subclass version.

    def move(self, grid):
        """Step forward one cell if it is open. Gives back True or False."""
        d_row, d_col = STEPS[self._heading]
        new_row = self._row + d_row
        new_col = self._col + d_col
        if not grid.is_open(new_row, new_col):
            self._log.append(f"blocked at ({new_row}, {new_col})")
            return False
        if self._battery < self.step_use():
            self._log.append("battery low")
            return False
        self._row = new_row
        self._col = new_col
        self._battery -= self.step_use()
        self._log.append(f"moved to ({new_row}, {new_col})")
        return True

That is the move() method from your rover.py. self is the rover that is moving. When self is a ScoutRover, self.step_use() finds ScoutRover's step_use() first.

# scout_step.py
from field_data import FIELD
from grid import FieldGrid
from rover import Rover


class ScoutRover(Rover):
    """A light rover that uses less battery per step."""

    def step_use(self):
        return 2


grid = FieldGrid(FIELD)
pip = Rover("Pip")
dot = ScoutRover("Dot")
pip.run("FF", grid)
dot.run("FF", grid)
print(pip.status())
print(dot.status())
print(pip.step_use(), dot.step_use())
PREDICT SCOUT_STEP.PY
  • Read the question.
  • Tap your answer.
What is Dot's battery after two steps?
What is Pip's battery after two steps?
Pip at (0, 2) facing E, battery 90
Dot at (0, 2) facing E, battery 96
5 2

Trace one move for Dot

Line in move()Value for Dot's first step
d_row, d_col = STEPS[self._heading]heading E gives (0, 1)
new_row, new_col(0, 1)
grid.is_open(0, 1)True, so no "blocked"
self._battery < self.step_use()100 < 2 is False
self._battery -= self.step_use()battery becomes 98
self._log.append(...)"moved to (0, 1)"
Which method of ScoutRover does move() call? Write its name.
Dot's log after the first step reads: ____
At your computer
1. Save scout_try.py as a new file named scout_step.py.
2. Add the step_use() method to ScoutRover and the Pip lines, as shown. Run it and check 90, 96, and 5 2.
3. Change one thing: make ScoutRover's step_use() return 3. Predict Dot's battery, then run.
4. Break it on purpose: rename the method to step_used and run. Read the output carefully. There is no traceback.
5. Fix the name back to step_use and run once more.

A typo with no traceback

Step 4 has no error message, which makes it sneaky. Here is the run.

# scout_typo.py
from field_data import FIELD
from grid import FieldGrid
from rover import Rover


class ScoutRover(Rover):
    """A light rover that uses less battery per step."""

    def step_used(self):
        return 2


grid = FieldGrid(FIELD)
dot = ScoutRover("Dot")
dot.run("FF", grid)
print(dot.status())
Dot at (0, 2) facing E, battery 90
READ THE TYPO RUN
  • Read the question.
  • Tap your answer.
Why is Dot's battery 90, not 96?
What kind of error is this?

Practice: a battery-hungry test rover

Wren builds a test rover that uses 30 per step. A short route can then check the battery low rule from week 6.

# heavy_try.py
from field_data import FIELD
from grid import FieldGrid
from rover import Rover


class HeavyRover(Rover):
    """A test rover that uses a lot of battery per step."""

    def step_use(self):
        return 30


grid = FieldGrid(FIELD)
bale = HeavyRover("Bale", 3, 0)
bale.run("FFFF", grid)
print(bale.get_log())
print(bale.status())
TRACE HEAVY_TRY.PY
  • Read the question.
  • Tap your answer.
How many cells does Bale move?
What is the last log entry?
What is Bale's battery at the end?
['moved to (3, 1)', 'moved to (3, 2)', 'moved to (3, 3)', 'battery low']
Bale at (3, 3) facing E, battery 10
StatementTrue or false?
Overriding step_use() in ScoutRover changes Pip's battery use too.?
move() is written once, in Rover, and still uses each rover's own step_use().?
A misspelled method name in a subclass always gives a traceback.?
An overriding method has the same name as the base class method.?
WHY THIS EXERCISEAn override replaces the base version only for objects of the subclass, and a misspelled name is just a new method.

One method in the subclass changed how a method in the base class behaves. Tomorrow you build fleet.py for real.

← Monday