Back to the Introduction to Computer Science outline The Course Maker
Introduction to Computer Science outline
Week 9 · Lecture outline

Week 9 — Lecture Outline · Lists

Introduction to Computer Science · CSCI 1101 Fall 2026 · Prof. Okafor Fictional sample

Course: Introduction to Computer Science — CS1 / Programming Fundamentals in Python (CSCI 1101) · Silver Oak University (fictional sample) · Prof. Okafor
Objective covered: Objective 6 — Use Python's core collections; this week: create, index, slice, mutate, and iterate lists, and reason about aliasing vs. copying.
SLOs touched: A (write and run a correct program) · B (trace code and predict list contents; find and fix a defect)
Meeting pattern: 2 studio sessions × 75 min = 150 min. Segment minutes below total ~150; scale to your own pattern.

Every code output, traced value, and list result in this outline was produced by actually running the code in Python — not hand-traced. Run them live in class; the outputs are exact. (The mutate-while-iterating result in Segment 5 is one most people — and most chatbots — get wrong by hand; we ran it.)


Week at a Glance

The week's big question "How do I store and change a whole collection of values under one name — and what happens when two names point at the same list?"
By the end of the week, students can… (1) create a list and read it with indexing and slicing (exclusive stop, just like strings); (2) mutate a list — nums[0] = 9 and the methods append, insert, pop, remove, sort, plus len; (3) iterate a list with for to total / average / find a max, and recognize the mutate-while-iterating gotcha; (4) reason about aliasing (b = a → one list, two names) vs. copying (b = a[:]), and use in for membership.
Key vocabulary list, element/item, index, negative index, slice, exclusive stop, mutable, mutation, append, insert, pop, remove, sort, len, iterate, for ... in, mutate-while-iterating, aliasing, reference, copy, [:], list(), in (membership), is vs. ==, IndexError
Materials slides (Deck 9), the week's readings + video links, a free online Python environment + Python Tutor (links in the readings), one approved chatbot (Gemini / Claude / ChatGPT) for the AI-critique moment and the tutorial
Timing note 8 segments, ~150 min total. Session 1 = Segments 1–4 (~75). Session 2 = Segments 5–8 (~75). This is a studio: build the list on the projector and have students type every example into the online environment with you, then run it.

Segment 1 — Hook & the Promise (8 min) · Session 1 opens

Hook. Put a single sticky-note on the slide labeled score. "This is everything we've done for eight weeks — one name, one value." Now put up a shoebox labeled scores with a row of cards inside: 88, 92, 79. "What if one name could hold all of them, in order, and you could add, remove, and rearrange the cards whenever you want?" That box is a list — Python's most-used container. Every real program is full of them: a list of scores, a playlist, a shopping cart, a feed of posts.

The promise (write it on the board): "By Friday you'll build a list, change it five different ways, loop over it to find the highest score — and you'll know the one trap that bites every beginner: when you write b = a, you don't get a copy, you get a second name for the same box."

Why it matters line (memory hook): "b = a is two names for one list; b = a[:] is a real copy."


Segment 2 — Creating, Indexing & Slicing a List (20 min)

Plain language first. A list is an ordered collection of values written inside square brackets, separated by commas. The values are elements (or items). Build one and check its length:

nums = [10, 20, 30]
print(nums)
print(len(nums))

Output (run-verified):

[10, 20, 30]
3

Indexing — you already know this from strings. Each element has an index starting at 0. Negative indexes count from the end (-1 is the last):

nums = [10, 20, 30]
print(nums[0])
print(nums[-1])

Output (run-verified):

10
30

"Same rule as string indexing in Week 3: the first item is index 0, not 1. Reaching past the end is the classic off-by-one error — nums[3] here would crash, and we'll meet that error in Segment 6."

Slicing — exclusive stop, same as strings. nums[1:3] means "from index 1 up to but not including index 3":

nums = [10, 20, 30, 40, 50]
print(nums[1:3])
print(nums[:2])
print(nums[2:])

Output (run-verified):

[20, 30]
[10, 20]
[30, 40, 50]

"The stop is excludednums[1:3] gives you indexes 1 and 2, not 3. This is the single most common slicing mistake; it's identical to string slicing, so you already practiced it. A slice gives you back a new list — it does not change the original."


Segment 3 — Mutation & List Methods (the code-along) (24 min)

Set it up: "Strings could be indexed but never changed — they're immutable. Lists are different: lists are mutable, which means you can change them in place. Open the editor; we're going to build a to-do list and push it around."

Mutation by index — rewrite a slot:

nums = [10, 20, 30]
nums[0] = 9
print(nums)

Output (run-verified):

[9, 20, 30]

"nums[0] = 9 reaches into the box and replaces the first card. The list is now permanently changed."

Code-along — build & mutate a to-do list, step by step (everyone types and runs each line):

Step 1 — start the list and append (adds to the end):

todo = ["email", "lunch"]
todo.append("gym")
print(todo)

Output:

['email', 'lunch', 'gym']

Step 2 — insert(i, x) puts an item at index i, shifting the rest right:

todo.insert(0, "coffee")
print(todo)

Output:

['coffee', 'email', 'lunch', 'gym']

Step 3 — pop() removes and returns the last item; pop(0) removes and returns the first:

done = todo.pop(0)
print(done)
print(todo)

Output:

coffee
['email', 'lunch', 'gym']

Step 4 — remove(x) deletes the first matching value (not an index):

todo.remove("gym")
print(todo)

Output:

['email', 'lunch']

Step 5 — sort() orders the list in place; len() counts it:

scores = [30, 10, 20]
scores.sort()
print(scores)
print(len(scores))

Output:

[10, 20, 30]
3

The method cheat-sheet — name them and put them on a slide:
| Method | What it does | Returns |
|---|---|---|
| append(x) | add x to the end | None (changes the list) |
| insert(i, x) | put x at index i | None |
| pop() / pop(i) | remove & return last / item at i | the removed item |
| remove(x) | delete the first x by value | None |
| sort() | order the list in place | None |
| len(list) | how many items (a function, not a method) | the count |

"Burn in one warning, straight from the Python docs: append, insert, remove, and sort return None — they change the list in place rather than handing a new one back. So nums = nums.sort() is a classic bug — it sorts the list and then throws it away by overwriting nums with None. We'll see exactly that next week's chatbots get wrong."


Segment 4 — Iterating a List: Total, Average, Max (20 min) · Session 1 closes (~75)

Set it up: "A list is most useful when you walk through every item. You already know the for loop from Week 6 — point it at a list and it hands you each element in turn."

Iterate to total and average (code-along):

scores = [80, 90, 100]
total = 0
for s in scores:
    total = total + s
print(total)
print(total / len(scores))

Output (run-verified):

270
90.0

"for s in scores: runs the body once per element, with s being 80, then 90, then 100. We accumulate into total. Note the average is 90.0, a float — because / always produces a float (Week 2). If your gut said 90, that's exactly why we run it."

Iterate to find the max (worked trace — predict, then run):

scores = [80, 95, 72, 88]
biggest = scores[0]
for s in scores:
    if s > biggest:
        biggest = s
print(biggest)
  • Trace: start biggest = 80. Compare 80>80? no. 95>80? yes → biggest = 95. 72>95? no. 88>95? no. End: 95.
  • Predicted output: 95. Run it. Actual output (run-verified):
95

"Start biggest at the first element, then let any larger value replace it. This 'walk and keep the best so far' pattern is everywhere — max, min, longest word, highest score."

Membership with in (quick):

scores = [80, 95, 72]
print(95 in scores)
print(60 in scores)

Output (run-verified):

True
False

"in asks a yes/no question — is this value somewhere in the list? It gives back a Boolean, perfect for an if."


Segment 5 — The Mutate-While-Iterating Gotcha (20 min) · Session 2 opens

Hook back in: "Last session you looped over a list safely. Today you'll write a loop that looks obviously correct, predict its output, and watch Python do something surprising — because changing a list while you loop over it is one of the nastiest beginner bugs, and it never crashes. It just silently gives the wrong answer."

The buggy program (put it on a slide; have the room predict first): "We want to throw out every 0 from a list of scores."

scores = [0, 0, 90, 0, 85]
for s in scores:
    if s == 0:
        scores.remove(0)
print(scores)
  • Most of the room predicts [90, 85] — every zero gone. (So did most chatbots when we asked.)
  • Run it. Actual output (run-verified):
[90, 0, 85]

"A zero survived. Why? The for loop walks by position. It looks at index 0 (a 0), removes it — and now every item shifts left by one. The loop moves on to index 1, but the item that used to be at index 1 already slid down to index 0 and got skipped. Removing while iterating makes the loop jump over neighbors."

The two clean fixes (show both, run both):

Fix 1 — loop over a copy with [:], mutate the original:

scores = [0, 0, 90, 0, 85]
for s in scores[:]:
    if s == 0:
        scores.remove(0)
print(scores)

Output (run-verified):

[90, 85]

Fix 2 — build a new list of the ones you want to keep (often the clearest):

scores = [0, 0, 90, 0, 85]
kept = []
for s in scores:
    if s != 0:
        kept.append(s)
print(kept)

Output (run-verified):

[90, 85]

"Rule of the week: don't add to or remove from a list while you're looping over it. Loop over a copy (scores[:]) or build a new list. The official Python tutorial says the same thing — it's 'simpler and safer to create a new list.'"

Misconception + cure:
- ❌ "My loop is obviously right, so the output must be [90, 85]."
Cure: a loop that mutates its own list skips elements. The only way to know what it really does is to run it — your gut and a hand-trace both lie here.


Segment 6 — Spot & Fix the Bug: Aliasing + IndexError (the debugging demo) (18 min)

Set it up: "Here's a program that's supposed to keep a safe backup of a list before changing it — but the 'backup' gets wrecked too. Let's debug it."

The buggy program (put it on a slide exactly as is):

scores = [88, 92, 79]
backup = scores
backup.append(100)
print("scores:", scores)
print("backup:", backup)

Debug it out loud:
1. Predict: most say backup is [88, 92, 79, 100] and scores is the untouched [88, 92, 79].
2. Run it. Actual output (run-verified):

scores: [88, 92, 79, 100]
backup: [88, 92, 79, 100]
  1. The bug: backup = scores did not make a copy. It made backup a second name for the same list (this is aliasing). Appending through one name shows up under both — there's only one box.
  2. The fix, then run to confirm — make a real copy with [:] (or list(scores)):
scores = [88, 92, 79]
backup = scores[:]
backup.append(100)
print("scores:", scores)
print("backup:", backup)

Output (run-verified):

scores: [88, 92, 79]
backup: [88, 92, 79, 100]

Prove it with is vs ==:

a = [1, 2]
b = a
print(a is b)
c = a[:]
print(a is c)
print(a == c)

Output (run-verified):

True
False
True

"a is b asks 'same box?' — True, they're aliases. a is c is Falsec is a separate box. But a == c is True — same contents. is checks identity (same object); == checks equality (same values). This is the is-vs-== distinction, and it's exactly the aliasing trap in one line."

A second quick bug (let the room solve it) — IndexError:

nums = [10, 20, 30]
print(nums[3])

Run-verified error:

IndexError: list index out of range

"Three items live at indexes 0, 1, 2 — there is no index 3. Off-by-one again. The fix is nums[2] (last item) or nums[-1]."


Segment 7 — Misconceptions Round-Up + Quick Interaction (20 min)

Name the misconceptions out loud, then cure each:

  • "b = a makes a copy of the list."
    Cure: it makes a second name for the same list — change one, you change both. Copy with b = a[:] or b = list(a). (The signature trap of the week.)
  • "nums[1:3] includes index 3."
    Cure: the stop is excluded — you get indexes 1 and 2. Same as string slicing.
  • "I can remove items from a list while looping over it."
    Cure: that skips elements. Loop over a copy (for x in nums[:]) or build a new list.
  • "nums = nums.sort() gives me the sorted list."
    Cure: sort() returns None and changes the list in place, so this overwrites nums with None. Just write nums.sort().
  • "remove(2) removes the item at index 2."
    Cure: remove(x) deletes by value (the first 2 it finds), not by index. Use pop(2) to remove by index.

Interaction — Predict-then-Run (rapid-fire, ~8 min): put four programs on a slide; students predict each output solo (30 sec), compare with a neighbor (1 min), then you run all four. Suggested with run-verified outputs:

a = [1, 2]; b = a; b.append(3); print(a)        # [1, 2, 3]
nums = [5, 3, 8]; nums.append(1); nums.sort(); print(nums)   # [1, 3, 5, 8]
nums = [10, 20, 30, 40]; print(nums[1:3])        # [20, 30]
nums = [4, 4, 5]
for n in nums:
    if n == 4:
        nums.remove(4)
print(nums)                                      # [4, 5]  (one 4 survives!)

Tally how many the room got right — celebrate the misses on the first and last ([1, 2, 3] aliasing; [4, 5] mutate-while-iterating) as "this is why we run code."


Segment 8 — Technology Workflow + AI-Critique, Callback & Hand-off (18 min) · Session 2 closes (~75)

Technology workflow — see aliasing with your own eyes:
1. Open the free online Python environment (link in the readings). Build a list and run a method; read what changed.
2. To watch aliasing, paste into Python Tutor and click forward:
python a = [1, 2] b = a b.append(3) print(a)
You'll see both a and b draw arrows to the same list box. Then change line 2 to b = a[:] and step again — now two separate boxes appear. That picture is the lesson.

AI-critique moment (students verify, not consume):

Paste this to an approved chatbot: "In Python, what does this print? a = [1, 2] / b = a / b.append(3) / print(a)" — and also ask "does nums.sort() return the sorted list?"
Then check by running both yourself. Chatbots routinely claim b = a makes a copy and that print(a) shows [1, 2] — it's actually [1, 2, 3]. And many will say nums.sort() returns the sorted list — it returns None. Your job all semester: the tool drafts, you run it and judge. This week's blind spot is aliasing; catch it by running.

Callback + tease:
- Callback: "Today: create, index, and slice a list; mutate it five ways; loop it to total/average/find a max; and the two traps — mutating while iterating, and b = a aliasing. The indexing and slicing were free — you already knew them from strings."
- Tease next week: "Lists are one of four core collections. Next week we meet the other three: tuples (like a list, but immutable — it can't change), dictionaries (look things up by a key instead of a number), and sets (no duplicates, fast membership). You'll learn to pick the right one for the job."

Hand-off (the week's graded work):
- Lecture Tutorial 9 (AI tutor, share-link submission) — creating, indexing/slicing, mutating, iterating, and aliasing vs. copying.
- Quiz 9, Discussion 9 ("Explain a List (and the Aliasing Trap)"), and Assignment 9 ("Working With Lists").
- Coding Lab 9 — "Build It, Mutate It, Alias It" — build & mutate a list, run a predict-then-run table, fix the aliasing / mutate-while-iterating bug, and watch b = a alias in Python Tutor.


Instructor FAQ — Common Stumbles

Student says / does Quick cure
"I made a backup with b = a but it changed too." b = a aliases — one list, two names. Copy with b = a[:] or b = list(a).
Thinks nums[1:3] includes index 3. Slicing stops before the second number → indexes 1 and 2 only. Same as strings.
"My remove-the-zeros loop left one zero." Mutating while iterating skips elements. Loop over nums[:] or build a new list.
Writes nums = nums.sort() and gets None. sort() changes the list in place and returns None. Just write nums.sort().
Uses remove(2) to delete index 2. remove(x) deletes by value; pop(2) deletes by index.
nums[3] on a 3-item list → crash. IndexError: indexes are 0–2. Use nums[2] or nums[-1].
Confuses is and ==. is = same object (same box); == = same contents. Aliases are is-equal; copies are only ==-equal.
"I'll just trust what the chatbot says the list contains." This week chatbots miss aliasing constantly. Run it and read what Python prints.

Scope flag

This outline stays within Objective 6, the lists half (create, index, slice, mutate, iterate; aliasing vs. copying; in membership; reading an IndexError). It uses only this-week and prior-week constructs — for loops (Week 6), if (Week 4), / float division (Week 2), and string-style indexing/slicing (Week 3). Tuples, dictionaries, and sets are Week 10 and only teased here; list comprehensions, del, deep copies, and nested-list data structures are out of CS1 scope. Python and its method/exception names (append, pop, IndexError, …) are referenced factually; the instructor and institution remain fictional. Every output shown was produced by running the code.

~ Prof. Okafor's edition · Fall 2026 · built with thecoursemaker.com