Comet sets four sample tokens on the workbench. "Pip drives to a spot and picks up the sample there," she says. "Let's give the Rover class a place to keep them."
Wren checks the field sketch. "What does the evidence say about room?" he asks. "Pip's tray has three slots. What happens on the fourth sample?"
"It should say no," Comet says. "Calmly. Nothing breaks."
Nova projects a tray with three empty slots above the desk. "How can I help?" she asks. "Build it in stages. Predict before every run."
Comet slides the keyboard over. "Lead programmer, the rover file is yours today."
Today rover.py grows. A Rover gets two new instance variables: _capacity, set to 3, and _samples, an empty list.
An object's state is its attributes and their values, and the object has a has-a relationship with them. A Rover now has samples.
A constructor sets the starting state, with a starting value for every instance variable. So both new variables go in __init__.
Below the plan are __init__ after the change and the first two new methods.
| Method | Changes Pip? | What it does |
|---|---|---|
| pick_up(sample) | yes (a mutator) | Adds the sample if there is room. Gives back True or False. |
| get_samples() | no (an accessor) | Gives back a copy of the list of samples. |
| total_grams() | no | Adds up the grams of the samples Pip holds. |
def __init__(self, name, row=0, col=0):
self.name = name
self._row = row
self._col = col
self._heading = "E"
self._battery = 100
self._capacity = 3
self._samples = []
Rover.rovers_built += 1 def pick_up(self, sample):
"""Add a sample if there is room. Gives back True or False."""
if len(self._samples) >= self._capacity:
return False
self._samples.append(sample)
return True
def get_samples(self):
return list(self._samples) # carry_two.py
from rover import Rover
from sample import Sample
pip = Rover("Pip")
print(pip.pick_up(Sample("S01", "basalt", 42, 0, 2)))
print(pip.pick_up(Sample("S02", "quartz", 18, 2, 4)))
print(len(pip.get_samples()), "samples")
print(pip.status()) True True 2 samples Pip at (0, 0) facing E, battery 100
Now add the last new method:
def total_grams(self):
total = 0
for s in self._samples:
total += s.grams
return total # carry.py
from rover import Rover
from sample import Sample
pip = Rover("Pip")
spots = [
Sample("S01", "basalt", 42, 0, 2),
Sample("S02", "quartz", 18, 2, 4),
Sample("S03", "clay", 65, 3, 0),
Sample("S04", "mica", 7, 4, 5),
]
for s in spots:
print(s.label, pip.pick_up(s))
print(len(pip.get_samples()), "samples")
print("Total:", pip.total_grams(), "g") S01 True S02 True S03 True S04 False 3 samples Total: 125 g
Change self._capacity = 3 to self._capacity = 4 in rover.py, save, and run carry.py again.
S01 True S02 True S03 True S04 True 4 samples Total: 132 g
Set it back to 3. Then break total_grams on purpose: change self._samples to self._sample in its for line. Save and run carry.py.
S01 True
S02 True
S03 True
S04 False
3 samples
Traceback (most recent call last):
File "carry.py", line 15, in <module>
print("Total:", pip.total_grams(), "g")
~~~~~~~~~~~~~~~^^
File "rover.py", line 58, in total_grams
for s in self._sample:
^^^^^^^^^^^^
AttributeError: 'Rover' object has no attribute '_sample'. Did you mean: '_samples'? Pip can carry samples now. Tomorrow you will see why get_samples hands out a copy.