Learning Goals
3 minBy the end of this lesson you can:
- Find the maximum value in a list without using
max(), by sweeping with a "best so far" variable. - Compute a sum and a count by setting a variable to
0before the loop and adding to it inside. - Spot duplicates by checking, for each item, whether it appears again later in the list.
Warm-Up
5 minYou've seen max() mentioned in passing — give it a list of numbers, get back the biggest. sum() adds them all up. Today we ignore those and write the loops ourselves. Why? Because tomorrow you'll need the same shape of loop for a task no built-in solves — and the only way to know that shape is to have written it once.
Predict-the-output. What does this print?
scores = [70, 92, 65, 88, 73] best = scores[0] for s in scores: if s > best: best = s print(best)
Show the answer
92
The loop pretends each score might be the new champion, and replaces best whenever something larger walks by. Starting at scores[0] matters — start at 0 instead and the trick still works for these numbers, but it would break the moment a list has all negatives. Always seed with a real value from the list.
1) Find the MAX in a list. 2) SUM all values. 3) COUNT how many times a target appears. 4) Find DUPLICATES. Each is a tiny sweep — the patterns repeat with small tweaks.
New Concept · Four Sweep Patterns
10 minPattern 1 · Best so far (max, min)
Set best to the first item. Walk the list. Whenever something beats the current best, replace it. At the end, best holds the answer.
best = numbers[0] for n in numbers: if n > best: best = n
Pattern 2 · Running total (sum, average)
Set total to 0. Walk the list. Add each number to total.
total = 0 for n in numbers: total = total + n
Pattern 3 · Running counter (count)
Set count to 0. Walk the list. Add 1 only when an item matches a condition (e.g. equals a target).
count = 0 for item in items: if item == target: count = count + 1
Pattern 4 · "Have I seen this before?" (duplicates)
Keep a second list of items already seen. For each item, check whether it's already in the seen list. If yes — duplicate. If no — add it to seen and move on.
seen = [] duplicates = [] for item in items: if item in seen: duplicates.append(item) else: seen.append(item)
The in keyword does the "is this item already in the list?" check — and you used .append() back in PY-L1-17 to grow a list one item at a time. Same tools, new combination.
Set up an answer variable before the loop. Walk the list with the loop. Update the answer inside the loop. Print the answer after the loop. Four steps, four patterns, four challenges.
Worked Example · The Four Sweeps
18 minSave the file as list_lab.py. We'll build all four sweeps in one file, sharing the same opening list so you can compare results.
Code · shared setup
# list_lab.py — four list sweeps, written from scratch # A class's quiz scores. Notice 88 and 73 appear twice — useful later. scores = [70, 92, 65, 88, 73, 88, 100, 73, 54, 92]
Task 1 · MAX — without max()
Daniel wants to know the highest score in the class. We sweep the list with a "best so far" tracker.
Code
# Task 1 — biggest number highest = scores[0] for score in scores: if score > highest: highest = score print("Highest:", highest)
Output
Highest: 100
Two tiny rules. Seed highest with a real list item, not 0 (negatives would break it). Update only when score > highest — the equality case wouldn't hurt, but the strict > is the cleanest reading: "strictly better than my current champion".
Task 2 · SUM — without sum()
Aisyah wants the class total to compute the average later. Running total — empty bowl before the loop, ladle one number in per pass.
Code
# Task 2 — running total total = 0 for score in scores: total = total + score print("Total:", total) print("Average:", total / len(scores))
Output
Total: 785 Average: 78.5
Once you have the total, the average is one extra line — total divided by count. We use len(scores) for the count because we want the program to keep working even if someone adds more scores to the list. Hard-coding 10 would silently lie the moment the list grew to eleven.
Task 3 · COUNT — how many times does a target appear?
Hafiz keeps getting 73 — is he the only one? Let's count how many students scored exactly 73.
Code
# Task 3 — count one target value target = 73 count = 0 for score in scores: if score == target: count = count + 1 print(target, "appeared", count, "times")
Output
73 appeared 2 times
The shape is almost identical to the running total — the only difference is that we add 1 (not the score) and only when the value matches. That conditional + 1 is the "running counter" pattern, and you'll use it everywhere: counting passes, counting upvotes, counting hits.
Task 4 · DUPLICATES — which items appear more than once?
For the prize-giving, Mei wants to know which scores were repeated — those students could break a tie with a quiz-off.
Code
# Task 4 — find duplicates by tracking what we've seen seen = [] duplicates = [] for score in scores: if score in seen: if score not in duplicates: duplicates.append(score) else: seen.append(score) print("Duplicates:", duplicates)
Output
Duplicates: [88, 73, 92]
Two helper lists. seen remembers every value we've already walked past. duplicates collects the repeats. The extra inner if score not in duplicates stops the same number being added twice if it shows up three or more times — try changing the list so a value appears four times, and watch duplicates still list it just once. Without that inner check you'd get [88, 73, 92, 92], which is itself confusingly duplicated.
One day you'll need to find "the student with the highest score and a name starting with A", or "the dish whose name is shortest". There's no built-in for those — but the "best so far" loop fits them perfectly. Built-ins handle the common case; understanding the pattern handles every case.
Try It Yourself · Twist the Sweeps
13 minPick at least two. Each starts from one of the four worked examples — tweak it, don't rebuild it.
Adapt the "highest" loop to find the lowest score. Change the seed name, change one comparison.
Hint
lowest = scores[0] for score in scores: if score < lowest: lowest = score print("Lowest:", lowest)
Two changes from Task 1: rename highest → lowest, flip > to <. The same shape now answers the opposite question — that's the value of patterns.
Using the running-counter pattern, count how many scores are at least 75 (a pass). Print the count.
Hint
passes = 0 for score in scores: if score >= 75: passes = passes + 1 print("Passes:", passes)
Same shape as Task 3 — the only thing that changes is the condition inside the if. We swapped == target for >= 75. Everything else is identical.
Given a list of dishes ordered at the kantin today, print the dish that was ordered most often. Combine the running counter with the "best so far" pattern: for each unique dish, count how many times it appears, and remember the dish with the highest count.
orders = ["nasi lemak", "roti canai", "nasi lemak", "satay", "roti canai", "nasi lemak", "mee goreng", "satay", "nasi lemak", "satay"]
Hint
best_dish = orders[0] best_count = 0 seen = [] for dish in orders: if dish in seen: continue seen.append(dish) count = 0 for d in orders: if d == dish: count = count + 1 if count > best_count: best_count = count best_dish = dish print("Most ordered:", best_dish, "—", best_count, "times")
Two loops — an outer one to pick each unique dish, an inner one to count it. The seen list stops us counting the same dish twice. continue jumps straight to the next pass of the loop, skipping the rest of the body. (You haven't seen continue formally yet — think of it as the partner of break.)
Mini-Challenge · The Class Report
8 minCikgu Aisyah needs a one-page report of the term's quiz scores for her Form 1 class. Build class_report.py:
scores = [82, 67, 91, 74, 67, 88, 91, 54, 79, 91, 67, 85]
Your file must print, in order:
- Total — the sum of all scores, computed with the running-total pattern (no
sum()). - Average — the total divided by
len(scores). - Highest — the biggest score, computed with the "best so far" pattern (no
max()). - Lowest — the smallest, same idea with
<instead of>. - Passes — how many scores are
>= 75, using the running-counter pattern. - Repeated scores — any value that appears more than once, using the
seen/duplicatespattern.
Stretch. Wrap each of the six computations in its own function with def — for example def find_max(values): that takes a list and returns the answer. This is a preview of PY-L1-26 (functions). Inside def you indent the body just like a loop or if.
Show one possible solution
# class_report.py — term quiz report, all written by hand scores = [82, 67, 91, 74, 67, 88, 91, 54, 79, 91, 67, 85] # Total total = 0 for score in scores: total = total + score # Average average = total / len(scores) # Highest highest = scores[0] for score in scores: if score > highest: highest = score # Lowest lowest = scores[0] for score in scores: if score < lowest: lowest = score # Passes passes = 0 for score in scores: if score >= 75: passes = passes + 1 # Repeated scores seen = [] duplicates = [] for score in scores: if score in seen: if score not in duplicates: duplicates.append(score) else: seen.append(score) print("Total: ", total) print("Average: ", average) print("Highest: ", highest) print("Lowest: ", lowest) print("Passes: ", passes) print("Repeated: ", duplicates)
The non-negotiables: every answer is computed with a for loop — no max(), min(), sum(), or .count() shortcuts. total and passes must be seeded to 0 before their loops. highest and lowest must be seeded to scores[0], never to 0 — otherwise an all-negative list would crash the "highest" logic and a list with no zeros would lie about the "lowest". The duplicates loop needs both lists (seen and duplicates) and the inner not in duplicates check.
Recap
3 minFour sweeps, one shape: seed before, walk through, update inside, print after. Once that pattern lives in your fingers, you can answer almost any "summarise this list" question — even ones no built-in solves. Built-ins are convenience, not magic; behind every max() in the Python source is a sweep that looks just like the one you wrote today.
Vocabulary Card
- best so far
- A variable that holds the current champion. Updated whenever a new value beats it. Used for max, min, longest, shortest.
- running total
- A variable seeded to
0before a loop, added to inside. Squashes a list of numbers to one number. - running counter
- Same shape as a running total, but adds
1only when anifcondition is met. Answers "how many". in(membership test)x in some_listisTrueifxappears in the list. Useful for "seen this before?" checks.
Homework
4 minCreate a new file called kantin_audit.py — an end-of-week audit of the kantin's drink sales.
sales = [12, 9, 15, 9, 8, 18, 12, 22, 9, 15, 7, 18] drinks = ["teh tarik", "kopi-O", "milo ais", "teh tarik", "limau ais", "milo ais", "teh tarik", "milo ais", "kopi-O", "milo ais", "teh tarik", "milo ais"]
Rules:
- Print the total cups sold across the week — running total over
sales, nosum(). - Print the best day (the highest entry in
sales) — "best so far" oversales, nomax(). - Print the worst day (the lowest entry) — "best so far" with
<, nomin(). - Count how many days sold at least 15 cups — running counter, no
.count(). - Print the drinks that were sold more than once — the duplicates pattern over
drinks.
Stretch. Pair the two lists with enumerate (PY-L1-16) to print "Day 1 · 12 · teh tarik", "Day 2 · 9 · kopi-O", … as a numbered table above the summary.
Bring kantin_audit.py next class — we'll meet string methods in PY-L1-23.
Sample · kantin_audit.py
# kantin_audit.py — week-end audit of kantin drink sales sales = [12, 9, 15, 9, 8, 18, 12, 22, 9, 15, 7, 18] drinks = ["teh tarik", "kopi-O", "milo ais", "teh tarik", "limau ais", "milo ais", "teh tarik", "milo ais", "kopi-O", "milo ais", "teh tarik", "milo ais"] # Total cups total = 0 for cups in sales: total = total + cups # Best day best = sales[0] for cups in sales: if cups > best: best = cups # Worst day worst = sales[0] for cups in sales: if cups < worst: worst = cups # Busy days (>= 15 cups) busy = 0 for cups in sales: if cups >= 15: busy = busy + 1 # Repeated drinks seen = [] repeats = [] for drink in drinks: if drink in seen: if drink not in repeats: repeats.append(drink) else: seen.append(drink) print("Total cups :", total) print("Best day :", best) print("Worst day :", worst) print("Busy days :", busy) print("Repeated :", repeats)
Each answer must come from a for loop you wrote — no sum/max/min/.count. total and busy seed at 0; best and worst seed at sales[0]. The duplicates loop keeps both seen and repeats, with the inner not in repeats guard so a drink sold three or more times still appears just once in the output. Your numbers will match the sample exactly if you copied the two lists.