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

Monday

Many kinds of rover
// Subclasses reuse and override; one call, many behaviors
⏱ about 30 min

Monday: Many Kinds of Rover

Comet spreads her sketch notebook across the workbench. Two new rovers fill the page: a small, quick scout and a wide hauler with a big sample bin.

"What can we make?" she asks. "I will copy rover.py twice, rename the class, and change a few numbers. Done by lunch."

Wren taps his clipboard. "What does the evidence say? rover.py passes every test we wrote. Two copies means three places to fix the next bug."

Nova hovers above the notebook and projects a small tree: one box at the top, two branches below. "How can I help?" she asks. "A new class can start from a class you already trust."

Comet chews her pencil, then turns to you. "Lead programmer, can a class start from another class?"

A class built on a class

A derived class, or subclass, names its base class in brackets: class ScoutRover(Rover):. Rover is the base class.

When you call a method on a ScoutRover, Python looks in ScoutRover first. If the method is not there, it looks in the base class, Rover.

This first ScoutRover has only a docstring as its body. It adds nothing of its own yet. Read it and predict before you run it.

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


class ScoutRover(Rover):
    """A light rover. For now it adds nothing of its own."""


grid = FieldGrid(FIELD)
dot = ScoutRover("Dot")
print(dot.status())
dot.run("FF", grid)
print(dot.status())
print(dot.get_log())
print(Rover.rovers_built)
PREDICT SCOUT_TRY.PY
  • Read the question.
  • Tap your answer.
ScoutRover has no methods of its own. What does the first print show?
After dot.run("FF", grid), what is the battery?
What does print(Rover.rovers_built) show?
Dot at (0, 0) facing E, battery 100
Dot at (0, 2) facing E, battery 90
['moved to (0, 1)', 'moved to (0, 2)']
1

Dot moved, logged its moves and counted itself in Rover.rovers_built. Every one of those lines of code lives in rover.py. ScoutRover inherited them all.

Where does each method come from?

Call in scout_try.pyLook in ScoutRoverLook in RoverCode that runs
ScoutRover("Dot")no __init__foundRover.__init__
dot.status()no statusfoundRover.status
dot.run("FF", grid)no runfoundRover.run
dot.get_log()no get_logfoundRover.get_log
At your computer
1. Open IDLE and your fieldsim folder. Make a new file and save it as scout_try.py.
2. Type scout_try.py, save it and run it with Run Module (F5). Check the four lines against the output above.
3. Change one thing: add ray = ScoutRover("Ray", 3, 0) under dot, then print(ray.status()) and print(Rover.rovers_built) at the end. Predict, then run.
4. Break it on purpose: delete (Rover) so the line reads class ScoutRover:. Run it and read the error calmly.
5. Put (Rover) back and run it once more.

This is the error you will see in step 4. Without a base class, ScoutRover has no status() to find.

# scout_oops.py
class ScoutRover:
    """A light rover. For now it adds nothing of its own."""


dot = ScoutRover()
print(dot.status())
Traceback (most recent call last):
  File "scout_oops.py", line 7, in <module>
    print(dot.status())
          ^^^^^^^^^^
AttributeError: 'ScoutRover' object has no attribute 'status'
Which kind of error does a class with no base class give when you call status()?
WHY THIS EXERCISEAn AttributeError means the object has no attribute by that name. Without (Rover), nothing gives ScoutRover a status() method.

Practice: a scout that turns

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


class ScoutRover(Rover):
    """A light rover. For now it adds nothing of its own."""


grid = FieldGrid(FIELD)
dot = ScoutRover("Dot", 2, 0)
dot.turn_right()
dot.run("FX", grid)
print(dot.get_heading())
print(dot.get_log())
print(dot.status())
TRACE SCOUT_TURNS.PY
  • Read the question.
  • Tap your answer.
Dot starts at (2, 0) facing E and turns right. Which way does it face?
Which class holds the turn_right() code that runs?
What is the second entry in the log?
What is the battery at the end?
S
['moved to (3, 0)', 'unknown command X']
Dot at (3, 0) facing S, battery 95
StatementTrue or false?
A subclass must repeat every method of its base class.?
class ScoutRover(Rover): makes Rover the base class.?
Python looks for a method in the subclass first, then in the base class.?
Making a ScoutRover still runs Rover.__init__ when ScoutRover has none.?
WHY THIS EXERCISEA subclass inherits every method it does not write itself, so Dot drove, turned and logged with Rover's code.

One line, class ScoutRover(Rover):, gave Dot every skill Pip has. Tomorrow you make Dot behave differently.