Unit 08 · lesson

Lists and Collections

Core path: 30 minutes

What changes when the program needs to manage many related values instead of inventing another variable for each one?

This approach does not scale well:

score1 = 80
score2 = 95
score3 = 72
score4 = 88

The values are related, but the program has no collection representing that relationship.

A list gives those values one ordered structure:

scores = [80, 95, 72, 88]

A list has both items and order

Because lists are sequences, the indexing rules from Lesson 1 apply:

scores = [80, 95, 72, 88]

print(scores[0])  # 80
print(scores[2])  # 72

Conceptually:

index    0   1   2   3
value   80  95  72  88

The value at an index can change while the position still exists.

Lists are mutable

scores = [80, 95, 72]
scores[1] = 100
print(scores)

Output:

[80, 100, 72]

Unlike strings, the list itself can be modified in place.

That difference becomes important when more than one part of a program refers to the same list.

Lesson 5 comes back to that aliasing problem.

append() changes the existing collection

scores = [80, 95]
scores.append(72)

Now:

[80, 95, 72]

The list grew by one item at the end.

This is a state change on the existing list object.

Compare that with string methods from Lesson 1, which normally return a new string because strings are immutable.

Removal needs a clear target

players = ["Nova", "Maya", "Kai"]
players.remove("Maya")

removes the first matching value.

But what should happen if the value is not present?

players.remove("Ghost")

raises ValueError.

So before removing uncertain data, you might check membership:

if "Ghost" in players:
    players.remove("Ghost")

The right behavior depends on the application contract. Do not blindly wrap everything in checks just to suppress errors.

Loop over the items when you need each item

scores = [80, 95, 72, 88]

for score in scores:
    print(score)

This reads naturally:

for each score in scores, print the score.

You are not using indexes because the current position does not matter for this task.

That is cleaner than:

for index in range(len(scores)):
    print(scores[index])

when all you needed was each value.

Lists connect directly to counters and accumulators

Week 5 gave you:

total = 0

Now the list supplies the repeated values:

total = 0

for score in scores:
    total += score

average = total / len(scores)

Trace scores = [80, 95, 72]:

start total = 0
+80 → 80
+95 → 175
+72 → 247
247 / 3 → 82.333...

Python has:

sum(scores)

and you will often use it. Understanding the loop mechanism first means sum() becomes an abstraction you understand rather than magic.

Empty collections create real edge cases

This code fails when scores is empty:

average = sum(scores) / len(scores)

because len(scores) is 0.

The list itself is perfectly valid:

scores = []

The failure happens because the later calculation assumes at least one item exists.

That is a useful pattern to notice:

valid data structure
+
invalid assumption about its state
=
failure

Build the collection while the program runs

scores = []

for round_number in range(1, 4):
    score = int(input(f"Round {round_number}: "))
    scores.append(score)

Trace a run with 80, 95, 72:

start []
append 80 → [80]
append 95 → [80, 95]
append 72 → [80, 95, 72]

The collection starts empty and becomes part of the program state over time.

That pattern is everywhere: user records, sensor readings, API results, log entries, test failures, robot waypoints.

Membership answers a different question from indexing

players = ["Nova", "Maya", "Kai"]

if "Nova" in players:
    print("Player found.")

in asks whether a matching value exists somewhere in the collection.

It does not tell you the index unless you ask separately.

Choose the operation that matches the question.

Lists can contain many kinds of values, but structure still matters

Python allows:

mixed = ["Nova", 850, True, 91.7]

That is legal.

But ask whether another developer can tell what each position means.

If those values are really fields of one player record, a dictionary may communicate the structure better.

That is Lesson 3.

Build a score collection and inspect its state

scores = []

for round_number in range(1, 4):
    score = int(input(f"Round {round_number}: "))
    scores.append(score)
    print(f"DEBUG scores={scores}")

Enter:

80
95
72

At each iteration, predict the list before the debug line runs.

Then remove the debug output after you understand the state change.

Add:

if scores:
    average = sum(scores) / len(scores)
    print(f"Average: {average:.1f}")
else:
    print("No scores recorded.")

The condition if scores: uses the truthiness of the list: non-empty lists are truthy, empty lists are falsy.

Here that shortcut matches the actual question: do we have any scores?

Before Lesson 3

You should be able to explain:

  • why a list is better than score1, score2, score3 for one collection;
  • why indexes start at zero;
  • how list mutability differs from string immutability;
  • what append() changes;
  • when direct iteration is cleaner than manual indexing;
  • why an empty list can still expose a later assumption bug.

Vocabulary lab

Flip the idea, not just the card

Explain the term before you reveal the back. Then compare your explanation with the definition, example, and warning.

1 / 5
Read all terms without animation
List
An ordered, mutable Python collection that can contain multiple values. Example: scores = [80, 95, 72]. Do not confuse it with: A string, which is an immutable sequence of characters.
Mutable
Able to be changed in place after creation. Example: scores[1] = 100 changes an existing list. Do not confuse it with: A string, whose characters cannot be replaced in place.
append()
A list method that adds one item to the end of an existing list. Example: scores.append(72). Do not confuse it with: Assigning a value to an existing index.
Membership
Whether a value exists in a collection. Example: 'Nova' in players evaluates to True when Nova is present. Do not confuse it with: Knowing the exact position of that value.
Empty Collection
A valid collection containing zero items. Example: scores = []. Do not confuse it with: A missing variable or syntax error.