Learning Goals
3 minBy the end of this lesson you can:
- Test membership with
ininstead of writing a search loop. - Find a position with
index(), guarding against theValueError. - Summarise a list of numbers with
len,sum,maxandmin.
Warm-Up · The Search Loop You Will Not Need
5 minSix lines to answer one yes-or-no question. Read it, then predict the output.
club = ["Mia", "Leo", "Ana"] found = False for name in club: if name == "Ana": found = True print(found)
Answer
Output
True
Correct, and completely unnecessary. The whole thing is print("Ana" in club) — one line, no flag variable to forget to reset.
Every loop you replace with a built-in is a loop that cannot have an off-by-one, a missing flag, or a break in the wrong place.
New Concept · Questions a List Can Answer
12 minLike asking the class monitor rather than counting heads yourself. Is Ana here? Where is she sitting? How many people brought a calculator? One question, one answer.
in — is it there?
club = ["Mia", "Leo", "Ana", "Hugo"] print("Ana" in club) print("Zara" in club) print("Zara" not in club) print("ana" in club)
Output
True False True False
The last line matters: matching is exact, so case counts. not in reads better than not (x in y).
index() — where is it?
club = ["Mia", "Leo", "Ana", "Hugo", "Ana"] print(club.index("Ana")) print(club.index("Ana", 3))
Output
2 4
index() raises rather than returning -1Strings have find(), which returns -1 when there is no match. Lists have no find() — index() raises ValueError instead. Different behaviour, same lesson: check with in first.
club = ["Mia", "Leo", "Ana"] wanted = "Zara" if wanted in club: print(wanted, "is at seat", club.index(wanted)) else: print(wanted, "is not in the club")
Output
Zara is not in the club
count() — how many?
votes = ["pizza", "pancake", "pizza", "noodles", "pizza"] print(votes.count("pizza")) print(votes.count("ice cream")) print(len(votes))
Output
3 0 5
count() returns 0 for something absent rather than raising — so unlike index(), it needs no guard.
Summarising numbers
marks = [78, 91, 65, 88, 72] print("How many:", len(marks)) print("Total: ", sum(marks)) print("Highest: ", max(marks)) print("Lowest: ", min(marks)) print("Average: ", sum(marks) / len(marks)) print(f"Average: {sum(marks) / len(marks):.2f}")
Output
How many: 5 Total: 394 Highest: 91 Lowest: 65 Average: 78.8 Average: 78.80
These are built-in functions, so they wrap the list: sum(marks), not marks.sum(). max() and min() work on text too, comparing alphabetically.
sum([]) / len([]) raises ZeroDivisionError, and max([]) raises ValueError. Guard with if marks: — an empty list is falsy, so that reads as "if there are any marks".
Why it matters. Vote tallies, high scores, class averages, checking a name is registered before letting them in — every one is a built-in rather than a loop.
Worked Example · A Canteen Vote
12 minThirteen votes, counted and reported without a single counting loop.
# canteen_vote.py - tally a vote using built-ins only votes = ["pizza", "pancake", "pizza", "noodles", "pizza", "ice cream", "pancake", "pizza", "noodles", "pancake", "pizza", "ice cream", "noodles"] options = ["pizza", "pancake", "noodles", "ice cream", "hot dog"] print(f"{len(votes)} votes cast") print() print(f"{'Dish':<14}{'Votes':>6}{'Share':>9}") print("-" * 29) counts = [] for dish in options: n = votes.count(dish) counts.append(n) share = n / len(votes) * 100 print(f"{dish:<14}{n:>6}{share:>8.1f}%") print("-" * 29) winner_votes = max(counts) winner = options[counts.index(winner_votes)] print(f"Winner: {winner} with {winner_votes} votes") print(f"Dishes nobody chose: {counts.count(0)}") if "hot dog" in votes: print("Hot dog got at least one vote") else: print("Hot dog got no votes at all")
Output
13 votes cast Dish Votes Share ----------------------------- pizza 5 38.5% pancake 3 23.1% noodles 3 23.1% ice cream 2 15.4% hot dog 0 0.0% ----------------------------- Winner: pizza with 5 votes Dishes nobody chose: 1 Hot dog got no votes at all
The winner line is the pattern worth keeping:
max(counts)finds the biggest number.counts.index(...)finds where it sits.- That position reads the matching name out of
options, because the two lists were built together.
pancake and noodles both have 3. If either had won, index() would return the first match and the other would never be mentioned. Real tallies need a tie check — worth adding once you meet dictionaries.
Try It Yourself
13 minMake a list of six registered pupils. Ask for a name, then say whether they are registered and, if so, at what position. Nothing may crash.
Hint
registered = ["Mia", "Leo", "Ana", "Hugo", "Ella", "Theo"] wanted = input("Name: ").strip().title() if wanted in registered: print("Registered, number", registered.index(wanted) + 1) else: print("Not registered.")
Start with a list of ten marks out of 100.
- Print how many, the total, the highest, the lowest and the average to two decimals.
- Print how many marks are exactly equal to the highest.
- Print the position of the first top mark, counting from 1.
- Guard the whole thing so an empty list prints "No marks yet" instead of crashing.
Mini-Challenge 🔥 · Debug: The Report That Crashed on an Empty Class
8 minTheo's summary works for his class, then crashes when he runs it for a class with nobody in it. There are three mistakes.
# summary.py - buggy
marks = []
print("Pupils:", len(marks))
print("Average:", sum(marks) / len(marks))
print("Top mark:", max(marks))
print("Top pupil is number", marks.index(max(marks)))
print("Any full marks?", marks.index(100))Answer
- No guard for an empty list.
sum([]) / len([])raisesZeroDivisionError, andmax([])raisesValueError. The whole report needsif marks:around it. index()used as a yes-or-no test.marks.index(100)raisesValueErrorwhenever nobody scored 100, which is most of the time. Asking "is it there?" is100 in marks.- The pupil number is off by one.
index()counts from 0, so a report for humans should printindex + 1.
The working version, which now handles both classes:
# summary.py - fixed marks = [] print("Pupils:", len(marks)) if marks: average = sum(marks) / len(marks) top = max(marks) print(f"Average: {average:.2f}") print("Top mark:", top) print("Top pupil is number", marks.index(top) + 1) print("Any full marks?", 100 in marks) else: print("No marks yet - nothing to report.")
Output
Pupils: 0 No marks yet - nothing to report.
Put marks = [78, 91, 100, 65] back and the full report prints. An empty list is the case nobody tests and every real program eventually meets.
Recap
3 minin answers whether an item is present, index() says where — and raises ValueError if it is absent, so check with in first. count() is safe and returns 0. len, sum, max and min are built-in functions that wrap the list, and none of them survive an empty one except len and sum.
Vocabulary Card
- membership test
x in items— True when the list holds that value.- index
- Method returning the position of the first match, or raising
ValueError. - built-in function
- Called around the list —
sum(items), notitems.sum(). - falsy
- An empty list counts as False, so
if items:means "if it has anything".
Homework
4 minWrite weather_week.py. Store seven daily temperatures in Celsius and a matching list of seven day names.
- Print the week as an aligned table.
- Print the hottest and coldest day by name, not by number.
- Print the average to one decimal place.
- Print how many days were above the average.
- Ask the user for a temperature and say whether any day hit it exactly, using
in. - Make the whole report safe if the temperature list is empty.
Sample · weather_week.py
# weather_week.py - a week of temperatures, summarised with built-ins days = ["Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu", "Ahad"] temps = [31, 33, 29, 34, 32, 30, 33] if not temps: print("No readings yet.") else: print(f"{'Day':<10}{'Temp':>6}") print("-" * 16) for i in range(len(days)): print(f"{days[i]:<10}{temps[i]:>5}C") print("-" * 16) hottest = max(temps) coldest = min(temps) average = sum(temps) / len(temps) print(f"Hottest: {days[temps.index(hottest)]} at {hottest}C") print(f"Coldest: {days[temps.index(coldest)]} at {coldest}C") print(f"Average: {average:.1f}C") above = 0 for t in temps: if t > average: above = above + 1 print(f"Days above average: {above}") print(f"Days at the maximum: {temps.count(hottest)}") raw = input("Check a temperature: ").strip() if raw.isdigit(): wanted = int(raw) if wanted in temps: print(f"Yes - {days[temps.index(wanted)]} hit {wanted}C") else: print(f"No day was exactly {wanted}C") else: print("That was not a whole number.")
Output
Day Temp ---------------- Isnin 31C Selasa 33C Rabu 29C Khamis 34C Jumaat 32C Sabtu 30C Ahad 33C ---------------- Hottest: Khamis at 34C Coldest: Rabu at 29C Average: 31.7C Days above average: 3 Days at the maximum: 1 Check a temperature: 33 Yes - Selasa hit 33C
Two days reach 33C and only Selasa is named, because index() stops at the first match — count() is there to show the total. The isdigit() guard is Section 6 · Strings, and it keeps a typed word from crashing the report.