← Back to course
Python 11-12 / Week 07 / Wednesday
3/6
Week 07 · The Fleet

Wednesday

Sim Lab: fleet.py
// Subclasses reuse and override; one call, many behaviors
⏱ about 30 min

Wednesday: Sim Lab: fleet.py

Comet has a plan for the hauler. "It carries six samples, so I need a new __init__," she says. "I will copy all of Rover's __init__ lines and change the 3 to a 6."

Wren reads over her shoulder. "What does the evidence say? Rover.__init__ already has nine lines. If we add a tenth next month, your copy will miss it."

Nova projects a short line in gold: super().__init__(name, row, col). "How can I help?" she asks. "A subclass can ask the base class to do its part first, then add its own."

Comet grins and flips to a clean page. "Lead programmer, you type. I will check names against our design."

super() reaches the base class methods. It is useful for calling a method you have overridden.

In ScoutRover.__init__, the line super().__init__(name, row, col) runs Rover's __init__ first. That sets the name, position, heading, battery, samples and log. Then the next line changes the capacity to 1.

An override can also extend the base method instead of replacing it. ScoutRover.status() calls super().status() and puts "Scout " in front.

  1. Make a new file in your fieldsim folder and save it as fleet.py. Do not change rover.py today.
  2. Stage 1: type the header and ScoutRover with __init__ and step_use(), shown next. Save it.
  3. Type check_scout.py in a new file. Predict, then run it.
  4. Stage 2: add the status() override to ScoutRover. Run check_scout.py again.
  5. Stage 3: add HaulerRover at the end of fleet.py. Then type and run fleet_demo.py.
  6. Change one thing, then break one thing on purpose and fix it.
  7. Run test_fieldsim.py to check that nothing in the old code broke.
# fleet.py
# Two kinds of rover built on Rover (week 7).
from rover import Rover


class ScoutRover(Rover):
    """Light and quick: little battery per step, carries one sample."""

    def __init__(self, name, row=0, col=0):
        super().__init__(name, row, col)
        self._capacity = 1

    def step_use(self):
        return 2
# check_scout.py
from field_data import FIELD
from fleet import ScoutRover
from grid import FieldGrid
from sample import Sample

grid = FieldGrid(FIELD)
dot = ScoutRover("Dot")
dot.run("FF", grid)
print(dot.status())
print(dot.pick_up(Sample("T1", "clay", 10)))
print(dot.pick_up(Sample("T2", "clay", 10)))
PREDICT CHECK_SCOUT.PY
  • Read the question.
  • Tap your answer.
Stage 1: what does the first line show?
Stage 1: Dot tries to pick up two samples. What do the next two lines show?
Stage 2 adds the status() override. What does the first line start with then?
Dot at (0, 2) facing E, battery 96
True
False

Stage 2: add these lines to ScoutRover, under step_use(). Run check_scout.py again.

    def status(self):
        return "Scout " + super().status()
Scout Dot at (0, 2) facing E, battery 96
True
False

Stage 3: add HaulerRover to the end of fleet.py, after two blank lines. Your fleet.py now matches the whole file below. Then type fleet_demo.py.

# fleet.py
# Two kinds of rover built on Rover (week 7).
from rover import Rover


class ScoutRover(Rover):
    """Light and quick: little battery per step, carries one sample."""

    def __init__(self, name, row=0, col=0):
        super().__init__(name, row, col)
        self._capacity = 1

    def step_use(self):
        return 2

    def status(self):
        return "Scout " + super().status()


class HaulerRover(Rover):
    """Heavy: more battery per step, carries up to six samples."""

    def __init__(self, name, row=0, col=0):
        super().__init__(name, row, col)
        self._capacity = 6

    def step_use(self):
        return 8

    def status(self):
        return "Hauler " + super().status()
# fleet_demo.py
from field_data import FIELD
from fleet import HaulerRover, ScoutRover
from grid import FieldGrid
from rover import Rover

grid = FieldGrid(FIELD)
pip = Rover("Pip")
dot = ScoutRover("Dot")
moss = HaulerRover("Moss")
pip.run("FF", grid)
dot.run("FF", grid)
moss.run("FF", grid)
print(pip.status())
print(dot.status())
print(moss.status())
print(Rover.rovers_built)
PREDICT FLEET_DEMO.PY
  • Read the question.
  • Tap your answer.
What is Moss's battery after two steps?
What does print(Rover.rovers_built) show?
Change one thing: make every "FF" in fleet_demo.py a "FFF". Cell (0, 3) is open. What is the hauler's battery then?
Pip at (0, 2) facing E, battery 90
Scout Dot at (0, 2) facing E, battery 96
Hauler Moss at (0, 2) facing E, battery 84
3

Here is the run after the change to "FFF". Check your prediction.

Pip at (0, 3) facing E, battery 85
Scout Dot at (0, 3) facing E, battery 94
Hauler Moss at (0, 3) facing E, battery 76
3

Now break one thing on purpose. Save a copy of fleet.py as fleet_w7.py first. Then, in fleet.py, delete the super().__init__(name, row, col) line inside ScoutRover only. Run fleet_demo.py and read the traceback from the bottom.

Traceback (most recent call last):
  File "fleet_demo.py", line 12, in <module>
    dot.run("FF", grid)
    ~~~~~~~^^^^^^^^^^^^
  File "rover.py", line 92, in run
    self.move(grid)
    ~~~~~~~~~^^^^^^
  File "rover.py", line 73, in move
    d_row, d_col = STEPS[self._heading]
                         ^^^^^^^^^^^^^
AttributeError: 'ScoutRover' object has no attribute '_heading'. Did you mean: 'get_heading'?
READ THE TRACEBACK
  • Read the question.
  • Tap your answer.
Why does Dot have no _heading?
Which line of fleet_demo.py started the trail?

Python adds a guess at the end: Did you mean get_heading? Here the guess is not the fix. The real clue is that _heading was never set.

Put the super().__init__ line back, or copy it from fleet_w7.py, and run fleet_demo.py again. Then run test_fieldsim.py from week 6. fleet.py is a new file, so every old test should still pass.

All FieldSim tests passed

fleet.py is done: two new kinds of rover in about thirty lines, built on code you already trusted. Tomorrow the whole fleet answers one call.

← Tuesday