Two sessions in, Python can store information and make decisions. Today it learns to do something a human would never have the patience for: repeat the exact same task, correctly, a thousand times in a row without complaining once.
I open with a simple pitch - what takes a person 100 steps takes Python three lines. Nobody quite believes that until they see it. So let's get there.
Same as always: open session3.py, keep it next to this article, type everything yourself. Don't copy-paste.
for Loops - Do This for Every Item
Picture a teacher taking attendance. For every student in the register - call their name, mark present, move to the next. They don't decide in advance how many times to repeat it; they just keep going until the register runs out. That's exactly what a for loop does.
students = ["Amina", "Brian", "Njeri", "Otieno", "Wanjiku"]
for student in students:
print(f"Good morning, {student}!")
Good morning, Amina!
Good morning, Brian!
Good morning, Njeri!
Good morning, Otieno!
Good morning, Wanjiku!
student is a variable that Python refills on every round - first it's "Amina", then "Brian", and so on, until the list runs out and the loop just stops. No counting, no bookkeeping. That's the whole job of a for loop.
range() - Looping Over Numbers Instead of a List
Most of the time you don't have a ready-made list - you just want to count. range() generates numbers on the fly.
for i in range(5):
print(i)
0
1
2
3
4
The one rule that catches everyone at least once: the stop value is never included. range(5) gives you 0 through 4, not 0 through 5. You can also give it a start and a step:
for i in range(1, 11, 2):
print(i, end=" ")
1 3 5 7 9
range(1, 11, 2) starts at 1, stops before 11, and jumps by 2 each time. That end=" " keeps everything on one line instead of stacking vertically - handy for output like this.
The Accumulator Pattern - Building a Total Inside a Loop
This is the pattern you'll use more than any other single idea in this course. Start a variable at zero before the loop, then add to it on every round.
transactions = [1500, 3000, 800, 12000, 450, 2700]
total = 0
for amount in transactions:
total = total + amount
print(f"Added Ksh {amount:,} - Running total: Ksh {total:,}")
print(f"Final total: Ksh {total:,}")
Added Ksh 1,500 - Running total: Ksh 1,500
Added Ksh 3,000 - Running total: Ksh 4,500
Added Ksh 800 - Running total: Ksh 5,300
Added Ksh 12,000 - Running total: Ksh 17,300
Added Ksh 450 - Running total: Ksh 17,750
Added Ksh 2,700 - Running total: Ksh 20,450
Final total: Ksh 20,450
total = 0 sitting above the loop is the empty bucket. Every round drops one more amount into it. This exact shape - set it to zero, add inside the loop, use it after - shows up constantly once you start working with real data.
while Loops - Keep Going Until...
A for loop is for when you know how many times to repeat, or you've got a list to walk through. A while loop is for when you genuinely don't know - you just keep going until a condition flips to False. Think of a teacher marking exactly 30 scripts (for) versus a security guard at a gate who checks IDs until their shift ends, however long that takes (while).
count = 1
while count <= 5:
print(f"Count: {count}")
count += 1 # must change count, or this never ends
print("Done!")
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Done!
That count += 1 isn't optional decoration - it's the whole reason the loop ever stops. Delete it and Python prints "Count: 1" forever. That's called an infinite loop, and it's the first real bug most people write in this course.
A hugely common shape is while True paired with break - run forever, and let the user decide when to stop:
total = 0
while True:
entry = input("Enter amount (or done to finish): ")
if entry.lower() == "done":
break
total += int(entry)
print(f"Running total: Ksh {total:,}")
print(f"Final total: Ksh {total:,}")
Enter amount (or done to finish): 1500
Running total: Ksh 1,500
Enter amount (or done to finish): 3000
Running total: Ksh 4,500
Enter amount (or done to finish): done
Final total: Ksh 4,500
while True never becomes False on its own - break is the only exit, and it fires the instant the user types "done."
break vs continue - Two Very Different Interrupts
Both interrupt a loop, but not in the same way. Think of a matatu: it breaks down, everyone gets off, the journey's over - that's break. One passenger has no fare, so they get skipped and the matatu carries on to the next stop - that's continue.
scores = [78, 85, 91, 35, 66, 55, 42]
for score in scores:
if score < 40:
print(f"First failing score found: {score}")
break
print(f" {score} - OK")
print("Search complete.")
78 - OK
85 - OK
91 - OK
First failing score found: 35
Search complete.
66, 55, and 42 never even get checked - break shut the whole loop down the moment it found what it was looking for. Compare that to continue, which skips just the current round and moves on:
scores = [78, -1, 85, -1, 91] # -1 means absent
for score in scores:
if score == -1:
continue
print(f" Score: {score}")
Score: 78
Score: 85
Score: 91
The absent entries are silently skipped, but the loop keeps running through the rest of the list. break ends things. continue just skips ahead.
enumerate() - Position and Item, Together
Sometimes you don't just need the item, you need to know where it sits - item number 1, item number 2, and so on. You could do this with range(len(...)), but it's clunky. enumerate() does it cleanly.
students = ["Amina", "Brian", "Njeri", "Otieno", "Wanjiku"]
for number, name in enumerate(students, start=1):
print(f"{number}. {name}")
1. Amina
2. Brian
3. Njeri
4. Otieno
5. Wanjiku
Every round now hands you two variables instead of one - the position and the item. start=1 just tells it to count from 1 instead of Python's default of 0. This combines naturally with everything you've learned so far:
scores = [78, 85, 45, 91, 38]
for position, score in enumerate(scores, start=1):
status = "FAIL" if score < 50 else "PASS"
print(f"Student {position}: {score:>3} {status}")
Student 1: 78 PASS
Student 2: 85 PASS
Student 3: 45 FAIL
Student 4: 91 PASS
Student 5: 38 FAIL
Nested Loops - A Loop Inside a Loop
A nested loop is a for loop living inside another for loop. The golden rule: for every single round of the outer loop, the inner loop runs all the way through, start to finish, before the outer loop moves on. Think of a school timetable - for each day, go through every period, then move to the next day.
days = ["Monday", "Tuesday", "Wednesday"]
periods = ["Period 1", "Period 2", "Period 3"]
for day in days:
print(f"--- {day} ---")
for period in periods:
print(f" {period}")
--- Monday ---
Period 1
Period 2
Period 3
--- Tuesday ---
Period 1
Period 2
Period 3
--- Wednesday ---
Period 1
Period 2
Period 3
Three days times three periods gives nine inner prints - the inner loop resets back to Period 1 every time the outer loop steps to a new day. That multiplication - outer rounds × inner rounds - is worth doing in your head before you run any nested loop, so you know roughly what to expect.
Bringing It All Together - Class Results Processor
This is where every idea from today lands in one program: a while True loop to collect an unknown number of scores, continue to skip invalid entries, break to finish when the user types "done," and enumerate to build the final report.
scores = []
print("Enter student scores. Type done when finished.")
while True:
entry = input("Score (or done): ")
if entry.lower() == "done":
break
try:
score = int(entry)
if score < 0 or score > 100:
print(" Invalid - must be 0 to 100. Skipping.")
continue
scores.append(score)
except:
print(" Not a number. Skipping.")
continue
if not scores:
print("No scores entered.")
else:
print(f"\n=== CLASS REPORT ({len(scores)} students) ===")
passed, failed = 0, 0
for num, score in enumerate(scores, start=1):
grade = "A" if score >= 80 else "B" if score >= 70 else "C" if score >= 60 else "D" if score >= 50 else "F"
status = "PASS" if score >= 50 else "FAIL"
if score >= 50:
passed += 1
else:
failed += 1
print(f" Student {num:>2}: {score:>3} Grade {grade} {status}")
print(f"\nTotal: {len(scores)}")
print(f"Passed: {passed} Failed: {failed}")
print(f"Average: {sum(scores)/len(scores):.1f}")
print(f"Highest: {max(scores)} Lowest: {min(scores)}")
Enter student scores. Type done when finished.
Score (or done): 87
Score (or done): 45
Score (or done): 91
Score (or done): 62
Score (or done): done
=== CLASS REPORT (4 students) ===
Student 1: 87 Grade A PASS
Student 2: 45 Grade F FAIL
Student 3: 91 Grade A PASS
Student 4: 62 Grade C PASS
Total: 4
Passed: 3 Failed: 1
Average: 71.2
Highest: 91 Lowest: 45
Type this one out slowly. Every line traces back to something covered today - collecting unknown input with while True, filtering bad data with continue, exiting cleanly with break, and numbering the report with enumerate.
Try It Yourself
Easy - classifier. Loop through [250, 980, 120, 1500, 75] and print each price along with "Affordable" (under 500) or "Expensive" (500+). You'll need an if/else living inside your for loop - that's completely normal.
Medium - Savings goal calculator. Ask for a savings goal and a monthly savings amount. Use a while loop to add the monthly amount each round, printing the running total, and stop as soon as the goal is reached. Print how many months it took. Test with a goal of Ksh 50,000 and Ksh 8,000 a month - you should land on 7 months and a final total of Ksh 56,000.
Challenge - Matatu route finder. Store three routes as a list of dictionaries, each with a route name and a list of stops. Use nested loops to print every route and its stops, numbered. Then ask the user for a stop name and search through every route to find which one serves it - using break the moment you find a match so you're not searching for no reason.
Top comments (0)