← Back to course
Python 11-12 / Week 07 / Thursday
4/6
Week 07 · The Fleet

Thursday

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

Thursday: One Call, Many Behaviors

Comet wants a fleet report at the end of every test run. Her first sketch is a long ladder of if and elif lines. Each rung checks the class name as a string.

"Every new kind of rover adds another rung," she admits. "That ladder is going to get tall."

Wren runs yesterday's demo again and points at the output. "What does the evidence say? status() already gave three different lines, and we never wrote an if."

Nova projects three rovers in a row with one arrow labeled status() touching each. "How can I help?" she asks. "Ask each object the same question. Let each class answer its own way."

Comet crosses out the ladder. "Lead programmer, show me the loop."

A list of rovers

A list can hold objects of different classes, as long as each one has the methods the loop calls. Every rover here has run() and status(), from Rover or from its own class.

One call, many behaviors: the loop calls rover.status() the same way every time, and each object answers with its own version. This is called polymorphism.

# fleet_report.py
from field_data import FIELD
from fleet import HaulerRover, ScoutRover
from grid import FieldGrid
from rover import Rover

grid = FieldGrid(FIELD)
fleet = [Rover("Pip", 3, 0), ScoutRover("Dot", 3, 0), HaulerRover("Moss", 3, 0)]
for rover in fleet:
    rover.run("FFFF", grid)
    print(rover.status())
PREDICT FLEET_REPORT.PY
  • Read the question.
  • Tap your answer.
Row 3 is open all the way across. Where does each rover end?
What is the scout's battery after four steps?
What is the hauler's battery after four steps?
Pip at (3, 4) facing E, battery 80
Scout Dot at (3, 4) facing E, battery 92
Hauler Moss at (3, 4) facing E, battery 68

Same call, different capacity

# fleet_load.py
from fleet import HaulerRover, ScoutRover
from rover import Rover
from sample import Sample

fleet = [Rover("Pip"), ScoutRover("Dot"), HaulerRover("Moss")]
for rover in fleet:
    taken = 0
    for n in range(8):
        if rover.pick_up(Sample(f"T{n}", "clay", 10)):
            taken += 1
    print(rover.name, taken, rover.total_grams())
PREDICT FLEET_LOAD.PY
  • Read the question.
  • Tap your answer.
Each rover is offered eight test samples of 10 g. What does Moss's line show?
pick_up() is written only in Rover. Why does Dot stop at 1?
Pip 3 30
Dot 1 10
Moss 6 60

isinstance asks "what kind?"

isinstance(obj, C) is True if obj is an instance of C or of a subclass of C. So a ScoutRover counts as a Rover too.

# kinds.py
from fleet import HaulerRover, ScoutRover
from rover import Rover

dot = ScoutRover("Dot")
pip = Rover("Pip")
print(isinstance(dot, ScoutRover))
print(isinstance(dot, Rover))
print(isinstance(pip, ScoutRover))
print(isinstance(dot, HaulerRover))
Line in kinds.pyPrints True?
isinstance(dot, ScoutRover)?
isinstance(dot, Rover)?
isinstance(pip, ScoutRover)?
isinstance(dot, HaulerRover)?
WHY THIS EXERCISEDot is a ScoutRover and so also a Rover. Pip is a plain Rover, and a scout is not a hauler.
True
True
False
False
At your computer
1. Type fleet_report.py, save it in your fieldsim folder and run it.
2. Change one thing: add ScoutRover("Ray", 0, 0) to the end of the list. Predict its line, then run. (Row 0 is open from (0, 0) to (0, 4).)
3. Type kinds.py and run it. Add print(isinstance(pip, Rover)) and predict before you run.
4. Break it on purpose: in fleet_report.py, put the string "Bay" in the list. Run it and read the last line of the traceback calmly.
5. Take "Bay" out and run once more.

This is the run for step 4. A string has no run() method, so the loop stops when it reaches it.

# fleet_report.py
from field_data import FIELD
from fleet import HaulerRover, ScoutRover
from grid import FieldGrid
from rover import Rover

grid = FieldGrid(FIELD)
fleet = [Rover("Pip", 3, 0), ScoutRover("Dot", 3, 0), "Bay"]
for rover in fleet:
    rover.run("FFFF", grid)
    print(rover.status())
Pip at (3, 4) facing E, battery 80
Scout Dot at (3, 4) facing E, battery 92
Traceback (most recent call last):
  File "fleet_report.py", line 10, in <module>
    rover.run("FFFF", grid)
    ^^^^^^^^^
AttributeError: 'str' object has no attribute 'run'
READ THE RUN
  • Read the question.
  • Tap your answer.
How many rover lines print before the error stops the loop?
Why does the string stop the loop?

The loop never asked which kind of rover it had. Each object knew how to answer. Tomorrow you trace exactly which method runs.

← Wednesday