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 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()) 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
# 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()) Pip 3 30 Dot 1 10 Moss 6 60
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.py | Prints True? |
|---|---|
| isinstance(dot, ScoutRover) | ? |
| isinstance(dot, Rover) | ? |
| isinstance(pip, ScoutRover) | ? |
| isinstance(dot, HaulerRover) | ? |
True True False False
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' The loop never asked which kind of rover it had. Each object knew how to answer. Tomorrow you trace exactly which method runs.