← Back to course
Python 11-12 / Week 04 / Friday
5/6
Week 04 Β· Objects Working Together

Friday

Trace Friday: Lists of objects
// Names, references and objects that hold objects
⏱ about 30 min

Friday: Trace Friday: Lists of Objects

Comet spreads the four field samples on the table. "I want a quick report," she says. "Total grams, the heaviest sample and how many are heavy."

"One loop can do all three," she adds, already reaching for the keyboard.

Wren slides his clipboard over first. "What does the evidence say after each pass?" he asks. "Trace it on paper, then we run it."

Nova projects an empty trace table with four rows. "How can I help?" she asks. "One row for each sample. Fill in the names as they change."

Comet grins and picks up a pencil. "Fine. Lead programmer, you call out the values."

One loop, three answers

A list of objects is walked like any list. Each pass, s refers to the next Sample, and the dot reaches its attributes and methods.

Python's own glossary says a list is more like an array in other languages. You will meet arrays when you read Java later in the course.

# report.py
from sample import Sample

samples = [
    Sample("S01", "basalt", 42, 0, 2),
    Sample("S02", "quartz", 18, 2, 4),
    Sample("S03", "clay", 65, 3, 0),
    Sample("S04", "mica", 7, 4, 5),
]

total = 0
heaviest = samples[0]
heavy_count = 0
for s in samples:
    total += s.grams
    if s.grams > heaviest.grams:
        heaviest = s
    if s.is_heavy():
        heavy_count += 1
print("Total:", total)
print("Heaviest:", heaviest.label)
print("Heavy:", heavy_count)
Passstotalheaviestheavy_count
before the loopnone yet0S010
1S0142S010
2S0260S010
3S03125S031
4S04132S031
TRACE REPORT.PY
  • Read the question.
  • Tap your answer.
What does the Total line show?
What does the Heaviest line show?
What does the Heavy line show?
Total: 132
Heaviest: S03
Heavy: 1
In which pass does heaviest change? Give the label of s in that pass.
ONE ROW OF THE TRACE
  • Read the question.
  • Tap your answer.
In pass 2, what is total after the line total += s.grams?

Find it, or None

Sometimes a search finds nothing. find_kind below returns the first sample of a kind, or None when no sample matches.

The two assert lines at the end are tests. If both pass, the last print runs.

# find_kind.py
from sample import Sample


def find_kind(samples, kind):
    """Precondition: samples is a list of Sample objects.
    Postcondition: gives back the first Sample of that kind, or None."""
    for s in samples:
        if s.kind == kind:
            return s
    return None


samples = [Sample("S01", "basalt", 42), Sample("S04", "mica", 7)]
hit = find_kind(samples, "mica")
print(hit.describe())
miss = find_kind(samples, "chalk")
print(miss)
assert find_kind(samples, "basalt").grams == 42
assert find_kind(samples, "chalk") is None
print("find_kind tests passed")
TRACE FIND_KIND.PY
  • Read the question.
  • Tap your answer.
What does print(hit.describe()) show?
What does print(miss) show?
S04 mica 7 g at (0, 0)
None
find_kind tests passed

Calling a method on None is a mistake. Here miss is None, and the program asks it to describe itself:

# none_trap.py
from sample import Sample

samples = [Sample("S01", "basalt", 42)]
miss = None
for s in samples:
    if s.kind == "chalk":
        miss = s
print(miss.describe())
Traceback (most recent call last):
  File "none_trap.py", line 9, in <module>
    print(miss.describe())
          ^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'describe'

List, dict or object?

A class bundles data and the functions that work on it. In FieldSim the crew picks a shape for each job. Choose the one that fits.

PICK THE SHAPE
  • Read the question.
  • Tap your answer.
Keep all four field samples so a loop can visit them one by one.
Look up a sample straight from its label, like S03.
Keep one sample's label, kind and grams together with describe().
At your computer
1. In your fieldsim folder, type report.py and run it. Check the three lines against your trace.
2. Add a lightest variable that works like heaviest, with < instead of >. Print its label, predict it, then run.
3. Type find_kind.py and run it. Change "chalk" to "basalt" on the miss line and see what prints.
4. Type none_trap.py and read the AttributeError calmly. Then add if miss is not None: above the last line, and indent the print under it.
  1. A name refers to an object. y = x adds a name; it does not copy.
  2. is checks the very same object; == checks equal values.
  3. A parameter is a new name for the object passed in.
  4. Pip has samples: a has-a relationship held in _samples.
  5. get_samples hands out a copy, so outside code cannot empty Pip by accident.
  6. list(x) and x.copy() make a shallow copy: a new list of the same objects.

Strong tracing this week. Tomorrow is a Mission Quest with sticky notes and boxes.

← Thursday