← Back to course
Python 11-12 / Week 07 / Friday
5/6
Week 07 · The Fleet

Friday

Trace Friday: Which method runs?
// Subclasses reuse and override; one call, many behaviors
⏱ about 30 min

Friday: Trace Friday: Which Method Runs?

Trace Friday starts with pencils, not keyboards. Wren rules four columns on graph paper: the call, the subclass, the base class, the code that runs.

Comet wants to skip ahead. "We know it works. The demo printed the right numbers."

"What does the evidence say?" Wren asks. "We saw the output. We have not shown why each number came out that way."

Nova projects a chain of three boxes, one under another, with an arrow climbing from the bottom box upward. "How can I help?" she asks. "Start at the object's own class. Climb until you find the name."

Comet sharpens her pencil and slides the paper to you. "Lead programmer, you trace. I will check."

The lookup rule

For a method call, Python searches the object's own class first. If the name is not there, it goes down the chain of base classes, one class at a time.

A base class method that calls another method, like move() calling self.step_use(), starts the search again from the object's own class.

Call on moss (a HaulerRover)In HaulerRover?In Rover?Code that runs
moss.run("FF", grid)noyesRover.run
self.move(grid), inside runnoyesRover.move
self.step_use(), inside moveyesyesHaulerRover.step_use
moss.status()yesyesHaulerRover.status
super().status(), inside statusskippedyesRover.status
USE THE LOOKUP RULE
  • Read the question.
  • Tap your answer.
moss.get_battery(): which code runs?
Inside Rover.move(), for moss, self.step_use() gives what?
Inside HaulerRover.status(), what does super().status() run?

Trace a longer chain

Wren adds a third level: a tiny test scout built on ScoutRover. Trace it on paper before you look at the output.

# mini_scout.py
from field_data import FIELD
from fleet import ScoutRover
from grid import FieldGrid
from sample import Sample


class MiniScout(ScoutRover):
    """A tiny test scout: one battery unit per step."""

    def step_use(self):
        return 1


grid = FieldGrid(FIELD)
bit = MiniScout("Bit")
bit.run("FF", grid)
print(bit.status())
print(bit.pick_up(Sample("T1", "clay", 10)))
print(bit.pick_up(Sample("T2", "clay", 10)))
Call on bitMiniScoutScoutRoverRover
__init__noyes (calls super)yes
step_useyesyesyes
statusnoyes (calls super)yes
pick_upnonoyes
TRACE MINI_SCOUT.PY
  • Read the question.
  • Tap your answer.
What does the first print show?
What do the two pick_up lines show?
Scout Bit at (0, 2) facing E, battery 98
True
False
PUT THE STEPS OF MINISCOUT("BIT") IN ORDER
  • ?Back in ScoutRover.__init__, the capacity is set to 1
  • ?ScoutRover.__init__ runs
  • ?Python looks for __init__ in MiniScout and does not find it
  • ?super().__init__ runs Rover.__init__, which sets the battery to 100
WHY THIS EXERCISEMiniScout inherits ScoutRover's __init__, which calls Rover's first and then sets the capacity.

Is-a and has-a

A ScoutRover is a Rover: isinstance(dot, Rover) is True. That is an is-a link, made by a subclass.

A Rover has samples. An object has a has-a relationship with its instance variables, like _samples, a list of Sample objects. That link is made by storing objects, not by a subclass.

StatementIs-a or has-a: true or false?
HaulerRover is a Rover.?
A Sample is a Rover.?
A Rover has a list of Samples.?
Sample should be a subclass of Rover, because rovers carry samples.?
WHY THIS EXERCISESubclasses are for kinds of a thing. Holding other objects in instance variables is has-a.

Read a test

Wren writes tests for the fleet in the same style as test_fieldsim.py. Read them and predict the output.

# test_fleet.py
# Tests for fleet.py. Run this file: it prints one line when all pass.
from field_data import FIELD
from fleet import HaulerRover, ScoutRover
from grid import FieldGrid
from rover import Rover


def test_scout():
    grid = FieldGrid(FIELD)
    dot = ScoutRover("Dot")
    assert dot.move(grid)
    assert dot.get_battery() == 98
    assert dot.status().startswith("Scout ")
    assert isinstance(dot, Rover)


def test_hauler():
    grid = FieldGrid(FIELD)
    moss = HaulerRover("Moss", 3, 0)
    moss.run("FFF", grid)
    assert moss.get_col() == 3
    assert moss.get_battery() == 76


test_scout()
test_hauler()
print("All fleet tests passed")
READ TEST_FLEET.PY
  • Read the question.
  • Tap your answer.
What does the file print when you run it?
All fleet tests passed
  1. A subclass names its base class in brackets and inherits every method it does not write.
  2. An override has the same name as the base method; for that subclass, its version runs.
  3. A base method that calls self.step_use() gets the version from the object's own class.
  4. super() reaches the base class version, to set up or to extend.
  5. One call on a list of mixed rovers gives each one's own behavior.
  6. Is-a comes from a subclass; has-a comes from storing objects.

You traced three levels of classes on paper and predicted every line. Tomorrow is the Mission Quest.

← Thursday