Learning Goals
3 minBy the end of this lesson you can:
- Read a short Python script and predict what it will print (or fail with) before running it.
- Spot common bug families: missing colon,
=vs==, off-by-one,Nonefrom missing return, wrong loop boundary. - Fix a bug while explaining why the original was wrong — not just what made it work.
The Rules
3 minFive buggy scripts ahead. For each one:
- Read it. Don't run it yet. Make a mental prediction — what will it print, or how will it crash?
- Run it. Compare the actual output to your prediction. Where were you wrong?
- Fix it. Make the smallest change that makes the script do what its comment claims it should do.
- Explain in one sentence why the bug was a bug. Type the sentence as a comment under your fix.
Open bug_hunt.py in your editor. Copy each broken bug below into the file with the bug name as a header (# ===== Bug 1 =====). Fix in place. The whole exercise should take about 35-40 minutes.
The point is to see the bug before you run the code. Programmers spend half their working life reading code that doesn't quite work — and the faster you spot the pattern, the better you get.
The Five Bugs
35 minBug 1 · The countdown that doesn't
This is supposed to count down from 5 to 1, then print "Go!":
# Should print 5, 4, 3, 2, 1, Go!
n = 5
while n > 0
print(n)
n = n + 1
print("Go!")Predict: what happens when you run it? Then fix.
Show the fix
n = 5 while n > 0: # missing colon print(n) n = n - 1 # was: n + 1 (infinite loop!) print("Go!") # Bug: missing colon = SyntaxError; n + 1 = counts UP forever.
Two bugs at once. The missing colon is a syntax error — Python refuses to run. Even after you add the colon, n = n + 1 makes the loop count up instead of down, so the condition n > 0 is true forever. Watch your direction inside loops.
Bug 2 · The function that does nothing
# Should print "HELLO" for each name.
def shout(name):
name.upper()
shout("Aisyah")
shout("Faiz")Show the fix
def shout(name): print(name.upper()) # was: name.upper() with no print shout("Aisyah") shout("Faiz") # Bug: .upper() returns a new string but doesn't change name — # we have to print (or assign) the result.
A classic string-immutability trap. name.upper() computes the upper-case version but throws it away — strings are immutable, so the method can't edit name in place. You must capture the return value with print(...) or name = name.upper().
Bug 3 · The always-true grade
# Should print "Pass" for score >= 50, "Fail" otherwise.
score = 30
if score = 50:
print("Pass")
else:
print("Fail")Show the fix
score = 30 if score >= 50: # was: score = 50 (assignment!) print("Pass") else: print("Fail") # Bug: = is assignment, == is equality, >= is the boundary check we want.
In Python, = in an if condition is a SyntaxError outright (older languages just silently set the variable, which is even nastier). The fix is >= for "at least 50". If you wrote ==, you'd only get a Pass for an exact 50, which is the next bug along.
Bug 4 · The list mystery
# Should print [1, 2, 3] then [1, 2, 3, 4] — independent lists. a = [1, 2, 3] b = a b.append(4) print(a) print(b)
Show the fix
a = [1, 2, 3] b = list(a) # was: b = a (same list, not a copy!) b.append(4) print(a) # [1, 2, 3] print(b) # [1, 2, 3, 4] # Bug: b = a aliases the SAME list. Use list(a) or a[:] to copy.
The same "lists are passed by reference" idea from PY-L1-31 and PY-L1-40. b = a doesn't copy — it makes b point to the same list. b.append(4) changes the one list both names refer to. Use list(a) or a[:] when you need an actual copy.
Bug 5 · The teacher's broken average
# Should print the average of a list of scores.
def average(nums):
total = 0
for n in nums:
total = total + n
return total / len(nums)
print(average([10, 20, 30])) # expect 20.0Show the fix
def average(nums): total = 0 for n in nums: total = total + n return total / len(nums) # un-indent: must be AFTER the loop # Bug: return was inside the loop, so it fired on the first item. # It returned 10/3 instead of 60/3. print(average([10, 20, 30])) # now correctly 20.0
Indentation is meaning in Python. With the return inside the loop, the function exits on the very first iteration — it never even sees the 2nd and 3rd numbers. Move return out one indent level so it runs after the loop completes.
Once you've cleared the first five, try this one. Should print only positive numbers:
for n in [-2, 3, -5, 7, 0]:
if n > 0:
print(n)
else:
continue
print("skipped a non-positive") # what's wrong?The print("skipped...") never runs because continue jumps straight back to the top of the loop. Fix: either delete the print, or swap their order so the print happens before the continue.
How to Read Code Like a Bug Hunter
7 minThree habits that turn anyone into a fast bug-finder. Use these on the next two Code Wars rounds too.
1. Read the comment, then check the code matches it
Every script has a comment at the top saying what it's supposed to do. Most bugs are obvious the moment you compare the code to its stated purpose — "the comment says count down, the code counts up". Don't skip the comment.
2. Track every variable's value on the first pass
Walk through the first iteration of any loop with pencil-and-paper. Write down what every variable holds after each line. Most bugs are off by exactly one — and an off-by-one is invisible if you only ever read the variable names. Watch the values.
3. Trust nothing about indentation
Python uses indentation to mean structure. A statement two spaces too far in or out lives in a different code block entirely. Whenever a function or loop misbehaves, the first thing to check is whether every line is at the right indent level.
(1) missing colon, (2) = vs ==, (3) off-by-one in range or while-condition, (4) function with no return (returns None), (5) list aliasing (b = a), (6) indent-level slip (especially return inside a loop), (7) wrong boolean operator (and vs or), (8) string method called without capturing the return. Most Level 1 bugs are one of these eight.
Self-Check
5 minYou've fixed all five (or four — be honest). Before moving on, answer in your head:
- For each bug, can you name which of the eight families above it belongs to?
- For each bug, did you write a one-line comment explaining why it was wrong?
- Could you write a brand-new buggy version of each bug — and would a classmate be able to fix it?
Yes? Move on. No? Re-read the worked example bug you struggled with and re-write the fix from scratch.
Bonus · The Diamond Bug
8 minFor students with time to spare. This one is sneaky. Predict carefully before running.
# Should print: "10 is even"
# "11 is odd"
def parity(n):
if n % 2 == 0:
return "even"
return "odd"
for n in [10, 11]:
if parity(n) == "even":
print(n, "is", parity(n))
elif parity(n) == "odd":
print(n, "is", parity(n))
elif:
print(n, "is mysterious")Show what's wrong
# Bugs: # 1. SyntaxError — elif without a condition. Python requires either # "elif <condition>:" or just "else:" with no condition. # 2. parity(n) is called THREE TIMES per iteration. Each one runs the # whole function fresh. For a small function it doesn't matter — for a # slow one (file read, database call), it's a serious waste. # Fix: for n in [10, 11]: label = parity(n) # call once, store result if label == "even": print(n, "is", label) elif label == "odd": print(n, "is", label) else: print(n, "is mysterious")
Two lessons in one bug. elif without a condition isn't valid syntax — change to else:. And calling a function multiple times to get the same answer is a small, common waste — assign once, reuse the variable. This habit will quietly speed up your code as functions grow more expensive.
Recap
3 minBug hunting is a skill, not a vibe. The same eight families turn up again and again — once you know their shapes you start spotting them before you even read the rest of the line. Today you fixed five (and maybe six or seven), but more importantly you started reading code with bug-hunter eyes: comparing to the comment, tracking values, checking indents. The next two Code Wars lessons sharpen the same skill from different angles — predicting output without running, then writing solutions against the clock. Bring the file you fixed today; we'll start tomorrow's lesson with a quick "explain your worst bug" round.
Vocabulary Card
- bug family
- A recurring kind of mistake — missing colon, off-by-one,
=vs==. Recognising the family is half the fix. - aliasing
- When two variables point to the same list/object. Edits via one are visible through the other.
- indent slip
- Code accidentally at the wrong indent level, putting it inside (or outside) a different block than intended.
- predicting before running
- The habit of guessing the output mentally before pressing Run. Trains you to read code, not just write it.
- fix-with-reason
- The discipline of explaining why the bug was a bug, not just patching the line until tests pass.
Homework
4 minTwo tiny tasks tonight — one looks back, one looks forward.
- Looking back. Go through your Hangman, Quiz Engine, Adventure files. Find one place in your own code where any of the eight bug families could have happened (even if it didn't). Write a comment above the line explaining what would have gone wrong.
- Looking forward. Write your own
bug_for_friend.py— a 10-line script with one deliberate bug from one of the eight families. Tomorrow you'll swap with a classmate (or do it solo and revisit in a week) and see if you can fix each other's bugs.
Bring both files next class. PY-L1-43 is the Predict-the-Output round — same idea, no fixing, just reading.
Sample · bug_for_friend.py
# bug_for_friend.py — find the bug in 1 minute or less.
# Should print every even number from 0 to 10.
for n in range(11):
if n % 2 = 0:
print(n)
# Bug family: "= vs ==" — the if-condition uses assignment instead of equality.
# Fix: change "=" to "==".The trick to writing a good practice bug is to make it small enough to read in 30 seconds, but with the bug nestled inside real-looking code so it doesn't scream "I'm the bug". Stick to one bug, give the script a clear "should do X" comment so the swap-partner has a target, and pick a family from the list of eight so the educational value is high.