MGU Computer Science Series — Programming with Python
Part II: Control Flow and Program Logic
Tracing · Errors · Testing · Debugging
Chapter 10 of 30
10
MGU Computer Science Series  ·  Programming with Python  ·  Library Textbook Edition

Chapter 10:
Tracing, Debugging,
and Testing Simple Programs

Reading code the way Python runs it — tracing tables, error messages, test cases, edge cases, and the habits that turn confusion into understanding

■ Part II of VII ■

■ 10 Sections  •  Chapter 10 of 30 ■

Consolidation chapter — closes Part II before functions begin in Chapter 11

Chapter Introduction and Learning Objectives

Every programmer, from beginner to expert, writes programs that do not work correctly on the first attempt. This is not a sign of failure. It is a normal part of programming. What separates programmers who grow from programmers who get stuck is not whether they make mistakes. It is whether they have the skills and habits to find and fix those mistakes.

This chapter is about exactly those skills. You have now written programs with variables, expressions, input and output, conditionals, and loops. Your programs have grown more complex. That means there are more places for something to go wrong — and more reasons to develop a disciplined approach to reading, tracing, and testing your programs.

Opening Idea

A program that runs without crashing is not the same thing as a program that is correct. Python will happily execute a flawed plan from beginning to end and print a confident, wrong answer. The only defense against this is a set of habits: tracing code carefully, reading error messages closely, testing with inputs you already know the answer to, and building programs in small, verified pieces. This chapter teaches those habits before your programs grow large enough that mistakes become hard to find.

10Sections
3Error Types
12Key Terms
30Total Chapters

Learning Objectives

By the end of this chapter, you should be able to:

  1. 1Trace a Python program step by step, tracking variable values, output, and execution order.
  2. 2Build and use a tracing table to follow a program through loops and conditionals.
  3. 3Read a Python traceback and identify the file, line number, error type, and message.
  4. 4Identify and fix common syntax errors, including missing colons and mismatched punctuation.
  5. 5Identify and fix common runtime errors, including ZeroDivisionError, ValueError, and NameError.
  6. 6Recognize logic errors and distinguish them from syntax and runtime errors.
  7. 7Use print-style debugging to observe variable values while a program runs.
  8. 8Write test cases that pair specific inputs with expected outputs.
  9. 9Identify edge cases for a given problem and explain why programs often fail on them.
  10. 10Apply incremental development to build and verify a program in small, tested pieces.

Sections at a Glance

Sections 10.1 – 10.3
Reading Programs Carefully: Tracing and Error Messages
Tracing · Tracing Tables · Tracebacks

Why Programmers Trace Code

When you read a sentence, your eyes move forward and your brain follows the meaning. When you trace code, you follow the computer — one instruction at a time — and track what changes.

Tracing is the practice of reading your program the way Python executes it: in order, keeping track of variable values, following the path through conditions and loops, and noting what gets printed or returned.

Beginners often skip tracing. They write code, run it, and then feel confused when the output is wrong. The confusion comes from not knowing what the program actually did. Tracing eliminates that confusion.

Example 10.1.1
score = 50
bonus = 10
score = score + bonus
print(score)

If you trace this program, you follow it line by line:

  • Line 1: score is assigned the value 50.
  • Line 2: bonus is assigned the value 10.
  • Line 3: Python evaluates score + bonus, which is 50 + 10 = 60. The result is assigned back to score. Now score is 60.
  • Line 4: Python prints 60.
Output
60

That was easy because the program is small. But the same approach applies when programs are larger. You read each line in the order Python runs it, you update your mental picture of every variable, and you follow every branch and every loop iteration.

Study Note

Tracing is not about memorizing what Python does. It is about thinking carefully about what each line means. Every skilled programmer traces code — sometimes in their head, sometimes on paper, sometimes by adding extra print statements to watch variables as a program runs.

Tracing Tables

A tracing table is a simple tool for keeping track of variable values as a program runs. You create a column for each variable, a column for any output, and a row for each step in the program. Then you fill it in as you read through the code.

Example 10.2.1
count = 0
total = 0

count = count + 1
total = total + 5

count = count + 1
total = total + 8

print(count)
print(total)

The variables are count and total. The tracing table looks like this:

StepLinecounttotalOutput
1count = 00—
2total = 000
3count = count + 110
4total = total + 515
5count = count + 125
6total = total + 8213
7print(count)2132
8print(total)21313

The program prints 2 and then 13.

Now trace a program with a loop:

Example 10.2.2
total = 0
for number in range(1, 5):
    total = total + number
print(total)

range(1, 5) produces the sequence 1, 2, 3, 4.

Iterationnumbertotal
Start—0
1st11
2nd23
3rd36
4th410

After the loop ends, print(total) outputs 10.

Why Tracing Tables Matter

When you trace carefully, you often discover the error before you even run the program. Students who skip tracing tend to change code randomly and feel lucky when it works. Programmers who trace code understand why it works — or why it doesn't.

Reading Error Messages

Python produces an error message whenever it cannot execute your program as written. The error message is not punishment. It is information. Learning to read error messages clearly is one of the most important debugging skills you can develop.

Here is a typical Python error message:

Traceback
Traceback (most recent call last):
  File "quiz.py", line 7, in <module>
    score = score + points
NameError: name 'points' is not defined

Every Python error message has the same structure:

Anatomy
The four parts of a Python traceback
  • TracebackThe word "Traceback" appears at the top. This tells you Python is showing you the sequence of steps that led to the error.
  • File & linePython tells you the file name (quiz.py) and the line number (line 7) where the error occurred. This is where you should start looking.
  • Problem linePython shows you the actual line of code that caused the problem: score = score + points.
  • Error & messageAt the bottom is the most important part — the error type (NameError) and a short message (name 'points' is not defined). This tells you exactly what kind of problem Python encountered.
Reading Strategy

Reading the bottom line first is often the fastest way to understand what went wrong. Once you know the error type, look at the line number, read that line in your program, and ask yourself why that error would occur there.

Sections 10.4 – 10.6
Three Kinds of Trouble: Syntax, Runtime, and Logic Errors
SyntaxError · ZeroDivisionError · ValueError · NameError · Logic Errors

Syntax Errors

A syntax error means that Python cannot even understand what you wrote. The code does not follow the rules of the Python language, so Python refuses to run the program at all.

Common causes of syntax errors:

  • Missing parentheses
  • Missing colons after if, for, while, or def
  • Mismatched quotation marks
  • Misspelled keywords
  • Extra or missing punctuation
Example 10.4.1
score = 85

if score >= 60
    print("Passing")

Running this produces:

  File "grades.py", line 3
    if score >= 60
                 ^
SyntaxError: expected ':'

Python points directly at the problem: the colon is missing at the end of the if statement.

Fix
if score >= 60:
    print("Passing")
Procedure
How to handle syntax errors
  • Step 1Read the error message and note the line number.
  • Step 2Look at that line and the line just before it. Python sometimes points one line past the actual mistake.
  • Step 3Look for the most common syntax problems: missing colons, missing parentheses, mismatched quotes.
  • Step 4Fix one problem at a time, then run again.
Good News

Syntax errors are actually the easiest errors to fix because Python tells you almost exactly where to look.

Runtime Errors

A runtime error is an error that Python does not detect until the program is actually running. The syntax was valid enough for Python to start, but something went wrong during execution.

Common runtime errors include:

  • Dividing by zero
  • Using a variable name that was never assigned a value
  • Trying to convert a string like "hello" to an integer
  • Using the wrong type of value where a different type is expected
Example 10.5.1 — ZeroDivisionError
items = 0
total = 100
average = total / items
print(average)
Traceback (most recent call last):
  File "average.py", line 3, in <module>
    average = total / items
ZeroDivisionError: division by zero

Python ran the first two lines fine, but failed at line 3 because dividing by zero is impossible.

Example 10.5.2 — ValueError
age = int("twenty")
print(age)
Traceback (most recent call last):
  File "age.py", line 1, in <module>
    age = int("twenty")
ValueError: invalid literal for int() with base 10: 'twenty'

The string "twenty" cannot be converted to an integer. Python expected digits, not letters.

Example 10.5.3 — NameError
print(result)
Traceback (most recent call last):
  File "result.py", line 1, in <module>
    print(result)
NameError: name 'result' is not defined

Python has never seen a variable named result. You are trying to use a variable that does not exist yet.

Procedure
How to handle runtime errors
  • Step 1Read the error type and message.
  • Step 2Find the line number in your code.
  • Step 3Ask: what value does the variable hold at that point? What operation is being performed? What could go wrong with those inputs?
  • Step 4Trace backward from the error to find where the bad value came from.

Logic Errors

A logic error is the most difficult kind of error to find. The program runs without crashing, but it produces the wrong result. Python cannot detect logic errors because, from Python's perspective, the program is valid and complete. The mistake is in your thinking, not in the syntax or the types.

Example 10.6.1
# Calculate the average of three scores
score1 = 80
score2 = 90
score3 = 70

average = score1 + score2 + score3 / 3
print(average)

Expected output: 80.0

Actual output: 193.33333333333334

The program ran perfectly. No errors. But the answer is wrong.

The problem is order of operations. Division has higher precedence than addition, so Python evaluated score3 / 3 first, producing 23.333..., and then added score1 + score2 + 23.333... to get 193.333....

Fix
average = (score1 + score2 + score3) / 3
print(average)

Now the output is 80.0.

Another classic logic error involves = versus ==:

password = "open123"

if password = "open123":   # This is actually a SyntaxError in Python
    print("Welcome!")

Python actually catches this specific mistake as a syntax error, but the confusion between assignment (=) and comparison (==) causes many logic errors in conditions that are harder to spot.

Example 10.6.2
# Count how many scores are passing (60 or above)
scores = [55, 70, 45, 90, 60]
passing = 0

for score in scores:
    if score > 60:       # Bug: should be >= 60
        passing = passing + 1

print(passing)

With the > instead of >=, a score of exactly 60 is not counted as passing. The program runs without error, but the result is wrong for any input containing a score of exactly 60.

Procedure
How to handle logic errors
  • Step 1Trace the program carefully. Build a tracing table and follow each step.
  • Step 2Add print statements to watch variable values as the program runs.
  • Step 3Test with inputs where you already know what the correct output should be.
  • Step 4Compare what you expected with what actually happened, line by line.
Why This Matters

Logic errors are dangerous precisely because nothing alerts you to them. A program with a syntax error or runtime error announces itself. A program with a logic error confidently hands you a wrong answer that looks correct unless you check it.

Test Cases

A test case is a specific input, together with the output you expect for that input. Writing test cases before you are done coding is one of the most important habits in programming.

A function to test
def is_passing(score):
    if score >= 60:
        return True
    else:
        return False

Test cases for this function:

Input (score)Expected OutputReason
90TrueWell above passing
60TrueExactly at the boundary
59FalseOne point below passing
0FalseLowest possible score
100TruePerfect score

To test each case manually:

print(is_passing(90))    # Expected: True
print(is_passing(60))    # Expected: True
print(is_passing(59))    # Expected: False
print(is_passing(0))     # Expected: False
print(is_passing(100))   # Expected: True

If any of these prints the wrong value, you have found a bug.

Why Test Cases Matter

Without test cases, you can only say "I think my program is correct." With test cases, you can say "My program produces the right output for these specific inputs." Test cases give you evidence, not just hope.

Good test cases cover:

  • A typical case in the middle (score of 90 above)
  • The boundary between two categories (score of 60 and 59)
  • Minimum and maximum realistic values
  • Any special or unusual input you can think of

Edge Cases

An edge case is an input that is at the boundary, extreme, or unusual end of what a program might receive. Edge cases are where programs most often fail, because programmers tend to test typical cases and forget the unusual ones.

Summing a List
Possible Edge Cases
What if the list is empty? What if it has exactly one item? What if all items are zero? What if all items are negative?
Dividing Numbers
Possible Edge Cases
What if the second number is zero?
Checking a Password
Possible Edge Cases
What if the user enters an empty string? What if the user enters a very long string?
Counting Characters
Possible Edge Cases
What if the word is empty? What if the word has only one character?

Consider this program that finds the largest number in a list:

def find_largest(numbers):
    largest = 0
    for number in numbers:
        if number > largest:
            largest = number
    return largest

Test with typical input:

print(find_largest([3, 7, 2, 9, 1]))   # Expected: 9

Output: 9. Looks correct.

Now test with an edge case — all negative numbers:

print(find_largest([-3, -7, -2, -9, -1]))   # Expected: -1

Output: 0. Wrong.

The Bug

largest = 0 assumes the largest value is at least zero. But if all numbers are negative, the function never finds anything larger than 0 and incorrectly returns 0.

A better approach
def find_largest(numbers):
    largest = numbers[0]    # Start with the first item, not zero
    for number in numbers:
        if number > largest:
            largest = number
    return largest

Now the function works correctly even when all numbers are negative.

The Habit

The habit of asking "what if the input is unusual?" and then testing that unusual input catches many bugs that normal testing misses.

Incremental Development

Incremental development means building a program in small pieces and testing each piece before moving to the next. Instead of writing the entire program and then running it for the first time, you write a small portion, test it, confirm it works, and then extend it.

Incremental development makes debugging far easier. When you write 50 lines and run the program for the first time, an error could be anywhere in those 50 lines. When you write 5 lines, test them, confirm they work, and then write 5 more, you always know that any new error is almost certainly in the new code you just added.

Here is an example. Suppose you want to build a program that reads a list of quiz scores from the user, then prints the total, the average, and the highest score.

Build Process
Building a quiz score summary, one piece at a time
  • Step 1Collect the scores and confirm they are stored correctly before adding any calculation logic.
  • Step 2Add the total. Run the program. Confirm the total is correct.
  • Step 3Add the average. Run the program. Check the average.
  • Step 4Add the highest score. Run the program one final time.
Step 1 — Collect the scores
scores = []
count = int(input("How many scores? "))

for i in range(count):
    score = float(input("Enter score: "))
    scores.append(score)

print("Scores entered:", scores)
Step 2 — Add the total
scores = []
count = int(input("How many scores? "))

for i in range(count):
    score = float(input("Enter score: "))
    scores.append(score)

total = sum(scores)
print("Total:", total)
Step 3 — Add the average
scores = []
count = int(input("How many scores? "))

for i in range(count):
    score = float(input("Enter score: "))
    scores.append(score)

total = sum(scores)
average = total / count
print("Total:", total)
print("Average:", average)
Step 4 — Add the highest score
scores = []
count = int(input("How many scores? "))

for i in range(count):
    score = float(input("Enter score: "))
    scores.append(score)

total = sum(scores)
average = total / count
highest = max(scores)

print("Total:", total)
print("Average:", average)
print("Highest:", highest)

Each step added one new idea. Each step was tested. If something broke, it was clear exactly where the new problem was introduced.

The Incremental Development Mindset

Get one part working before building the next.

Test early and test often.

Never add new code to code that is already broken.

If something stops working, think about what you just changed.

Chapter Summary

Key Terms

syntax error
An error that prevents Python from parsing your code because it violates the rules of the Python language. Always detected before the program runs.
runtime error
An error that occurs while a program is executing. The syntax was valid, but something went wrong during execution.
logic error
An error in which the program runs without crashing but produces the wrong output because the programmer's reasoning was incorrect.
traceback
The error report Python displays when a runtime error occurs, showing the file name, line number, and type of error.
tracing
The practice of reading program code one step at a time, tracking variable values, loop iterations, and branch decisions exactly as Python would execute them.
tracing table
A structured table used to record variable values, output, and execution steps as you trace through a program.
print-style debugging
The practice of adding temporary print() statements to a program to display variable values and help locate the source of a bug.
test case
A specific input paired with the expected output, used to verify that a program or function behaves correctly for that input.
edge case
An input that is at the boundary or extreme of what a program might receive — often where bugs hide.
incremental development
A programming practice in which you build a program in small pieces, testing each piece before adding the next.
NameError
A runtime error that occurs when Python encounters a variable name that has not been assigned a value.
ValueError
A runtime error that occurs when a function receives a value of the right type but an inappropriate value.
PracticeCode Reading

Read each program below carefully. Answer the questions without running the program. Then verify your answers by running the program.

Exercise 1

x = 10
y = 3
z = x // y
print(z)
print(x % y)
  1. What is the value of z?
  2. What does x % y evaluate to?
  3. What does this program print?

Exercise 2

count = 5
while count > 0:
    print(count)
    count = count - 2
  1. Trace through this loop. What values does count take?
  2. When does the loop stop?
  3. What exactly does this program print?

Exercise 3

result = 0
for i in range(1, 6):
    if i % 2 != 0:
        result = result + i
print(result)
  1. Which values of i satisfy the condition i % 2 != 0?
  2. What does result equal after the loop?
  3. What does the program print?

Exercise 4

word = "programming"
count = 0
for letter in word:
    if letter in "aeiou":
        count = count + 1
print(count)
  1. What is the program counting?
  2. Trace through the word character by character. Which characters increment count?
  3. What does the program print?
PracticeCode Tracing

Complete a tracing table for each program below.

Exercise 5

a = 1
b = 10
while a < b:
    a = a * 2
    b = b - 1
print(a)
print(b)

Build a tracing table with columns for a, b, and the loop condition. Trace each iteration. What does the program print?

Exercise 6

total = 0
count = 0

numbers = [4, 7, 2, 9, 3]
for number in numbers:
    if number > 5:
        total = total + number
        count = count + 1

print(total)
print(count)

Trace the values of total and count after each iteration. What does the program print?

Exercise 7

password = ""
attempts = 0

while password != "open" and attempts < 3:
    password = input("Enter password: ")
    attempts = attempts + 1

if password == "open":
    print("Access granted.")
else:
    print("Access denied.")

Trace this program if the user enters "wrong", then "wrong", then "open". What does the program print? Now trace it if the user enters "wrong" three times. What does the program print?

PracticePredict the Output

For each program, predict exactly what will be printed before running it.

Exercise 8

for i in range(3):
    for j in range(3):
        print(i, j)

Exercise 9

x = 100
y = 0

if x > 50:
    y = y + 10
if x > 75:
    y = y + 10
if x > 90:
    y = y + 10

print(y)

Exercise 10

values = [10, 20, 30, 40, 50]
result = values[0]
for i in range(1, len(values)):
    result = result + values[i]
    print(i, result)
PracticeDebugging and Error Analysis

Each program below contains an error. Identify the type of error (syntax, runtime, or logic), explain what the error is, and write the corrected code.

Exercise 11

total = 0
for i in range(1, 11)
    total = total + i
print(total)

What type of error is this? What is missing? Write the corrected program.

Exercise 12

items = []
average = sum(items) / len(items)
print(average)

What type of error is this? When does it occur? What would you need to check before computing the average? Write a corrected version that handles an empty list.

Exercise 13

# Count how many numbers are greater than the average
numbers = [10, 20, 30, 40, 50]
total = 0
for number in numbers:
    total = total + number
average = total / len(numbers)

above_count = 0
for number in numbers:
    if number > average:
        above_count = above_count + 1

print("Numbers above average:", above_count)

This program runs without error. Is the output correct? What should it print for this list? Trace through the second loop carefully.

Exercise 14

# A program to reverse a string
word = "python"
reversed_word = ""
for i in range(len(word)):
    reversed_word = word[i] + reversed_word
print(reversed_word)

Trace through this loop. What does reversed_word look like after each iteration? Does the program produce the correct result?

Exercise 15

# Find the smallest number in a list
def find_smallest(numbers):
    smallest = 0
    for number in numbers:
        if number < smallest:
            smallest = number
    return smallest

print(find_smallest([5, 3, 8, 1, 4]))
print(find_smallest([-2, -5, -1, -9]))

What does this function return for each call? Is that correct? Identify the logic error and fix it.

Worked ExampleBuilding and Testing a Quiz Score Analyzer

This worked example shows how to build a small program incrementally, test it with test cases, and use debugging to find and fix a logic error.

Goal: Write a program that takes a list of five quiz scores, then prints the total, the average, and whether the student is passing (average of 60 or above).

Step 1 — Define the scores and compute the total
scores = [70, 55, 80, 65, 90]
total = 0

for score in scores:
    total = total + score

print("Total:", total)

Test this. Expected total: 70 + 55 + 80 + 65 + 90 = 360. Output: Total: 360. Correct.

Step 2 — Add the average
scores = [70, 55, 80, 65, 90]
total = 0

for score in scores:
    total = total + score

average = total / len(scores)
print("Total:", total)
print("Average:", average)

Expected average: 360 / 5 = 72.0. Output: Average: 72.0. Correct.

Step 3 — Add the passing check
scores = [70, 55, 80, 65, 90]
total = 0

for score in scores:
    total = total + score

average = total / len(scores)
print("Total:", total)
print("Average:", average)

if average > 60:
    print("Result: Passing")
else:
    print("Result: Not Passing")

Test with the existing scores. Average is 72.0, which is above 60, so "Passing" is printed. Correct.

Step 4 — Test edge cases

Try an average of exactly 60. Change the scores to [60, 60, 60, 60, 60].

Expected output: Passing (60 is a passing average).

Bug Found

The condition is average > 60, not average >= 60. With an average of exactly 60, the program prints "Not Passing." That is a logic error.

Fix
if average >= 60:
    print("Result: Passing")
else:
    print("Result: Not Passing")

Test cases used:

ScoresExpected AverageExpected Result
[70, 55, 80, 65, 90]72.0Passing
[60, 60, 60, 60, 60]60.0Passing
[50, 55, 45, 50, 60]52.0Not Passing
[100, 100, 100, 100, 100]100.0Passing
[0, 0, 0, 0, 0]0.0Not Passing
Lesson

The boundary case (average of exactly 60) revealed the logic error. Without testing that case, the program would have appeared correct.

PracticeShort Programming Tasks
  1. Task 1. Write a program that reads five integers from the user and prints the total. Then add test cases: test it with five zeros, with five negative numbers, and with the numbers 1 through 5.
  2. Task 2. Write a program that asks the user to enter a number between 1 and 10. Use a loop to keep asking until the user enters a valid number. Test your program by entering invalid values first, then a valid value.
  3. Task 3. The following program should count how many even numbers appear in a list. Find the bug and fix it.
    numbers = [1, 2, 3, 4, 5, 6, 7, 8]
    even_count = 0
    
    for number in numbers:
        if number % 2 = 0:
            even_count = even_count + 1
    
    print(even_count)
  4. Task 4. Write a program that asks the user for a number and prints whether it is positive, negative, or zero. Write at least four test cases before you finish.
  5. Task 5. The following program is supposed to print the numbers from 10 down to 1. Trace through it and identify the bug.
    count = 10
    while count > 0:
        print(count)
    count = count - 1
    Fix the indentation error and verify your fix by tracing the corrected program.
  6. Task 6. Write a function called is_valid_age(age) that returns True if age is an integer between 0 and 120 inclusive, and False otherwise. Write test cases for typical values, boundary values (0 and 120), and out-of-range values (-1 and 121).
Mini-ProjectDebug and Improve a Quiz Program

Below is a quiz program that has multiple errors. Some are syntax errors, some are logic errors, and one will cause a runtime error under certain inputs. Your job is to find all of them, fix them, add proper test cases, and leave the program in a clean, working state.

Original (buggy) program
# Quiz program - 3 questions

score = 0

print("Welcome to the Python Quiz!")
print()

# Question 1
answer = input("What keyword starts a conditional in Python? ")
if answer == "if":
    score = score + 1
    print("Correct!")
else
    print("Incorrect. The answer is: if")

print()

# Question 2
answer = input("What function displays output in Python? ")
if answer = "print":
    score = score + 1
    print("Correct!")
else:
    print("Incorrect. The answer is: print()")

print()

# Question 3
answer = input("What is 7 times 8? ")
if answer == 56:
    score = score + 1
    print("Correct!")
else:
    print("Incorrect. The answer is: 56")

print()
print("Quiz complete!")
print("Your score:", score, "out of 3")

percentage = score / 3 * 100
print("Percentage:", percentage)

if percentage > 70:
    print("Good job!")
elif percentage > 50:
    print("Keep practicing.")
else:
    print("Review the material and try again.")
Your Tasks
Seven steps to a clean, working quiz program
  • Task 1Read through the entire program before running it. Identify every error you can find just by reading.
  • Task 2Classify each error as a syntax error, a runtime error, or a logic error.
  • Task 3Fix all errors.
  • Task 4Identify any edge case that might cause a problem (hint: what happens if the user enters nothing for Question 3, or enters a number that is typed as text?).
  • Task 5Add a test session where you deliberately enter wrong answers and verify that your score and percentage calculations are correct.
  • Task 6Add a comment above each question that notes what type of answer the program expects (string or number).
  • Task 7Extend the quiz with one additional question of your own.
Mixed ReviewMixed Control Flow Review

These questions review concepts from Chapters 5 through 10.

  1. Review 1. Write a program that asks for a temperature in Celsius and prints whether it is below freezing (less than 0), cold (0–14), mild (15–24), warm (25–34), or hot (35 or above).
  2. Review 2. Write a program that uses a while loop to ask the user for a positive number. Keep asking until the user enters a value greater than zero. Then print the square root of that number. Hint: x ** 0.5 computes the square root of x.
  3. Review 3. Write a program that prints all multiples of 7 between 1 and 100, one per line.
  4. Review 4. Write a program that reads 10 numbers from the user and then prints: the total, the average, the count of numbers above the average, and the count of numbers below the average.
  5. Review 5. The following program should print only the letters in a word that appear before the letter m in the alphabet (that is, the letters a through l). Find the logic error.
    word = "programming"
    for letter in word:
        if letter < "m":
            print(letter)
    Is this program correct? Trace through it. Does Python's comparison of single characters work the way you expect? Test it with a few different words.
ReviewReview Questions
  1. What is the difference between tracing code on paper and running the code? Why is tracing useful even when you can just run the program?
  2. Describe the three types of Python errors — syntax, runtime, and logic — and give one example of each.
  3. Why does Python catch syntax errors before runtime errors?
  4. A program crashes with this message: TypeError: can only concatenate str (not "int") to str. Explain what this error means. What likely caused it? How would you fix it?
  5. What is print-style debugging? What are two rules to follow when using it?
  6. Explain the difference between a test case and an edge case. Why do programs often fail at edge cases?
  7. Describe incremental development. Why does it make debugging easier compared to writing the entire program before testing anything?
  8. Consider this loop:
    n = 1
    while n != 10:
        n = n + 3
    print(n)
    Will this loop ever end? Trace through several iterations. What happens?
  9. A student writes this function:
    def find_maximum(numbers):
        maximum = 0
        for number in numbers:
            if number > maximum:
                maximum = number
        return maximum
    The student tests it with [3, 7, 2, 9, 1] and gets 9. They think the function is correct. What test case would reveal that it has a bug? What is the bug?
  10. What is the benefit of writing test cases for a function before you finish writing the function itself?
Preview — Chapter 11
Defining and Calling Functions

Chapter 10 closes the control flow unit by giving you the skills to verify and repair programs you write. You now have a systematic way to trace, classify errors, test, and debug. Chapter 11 opens Part III by introducing functions — named, reusable blocks of code that let you organize larger programs. You have been writing short scripts where all the code runs from top to bottom. Functions allow you to give a block of code a name, call it from multiple places, and avoid repeating the same instructions throughout a program. You will learn how to define a function, how to call a function, how execution moves into and back out of a function, and how functions help you organize programs that would otherwise become long and hard to follow. The tracing and debugging skills you built in Chapter 10 will carry directly into Chapter 11, because function calls add a new dimension to tracing: you will track not just variable values, but where in the program execution is happening.

MGU Library Connection
  • Programming with Python — Appendix D: Common Python Error Messages
  • Programming with Python — Appendix E: Debugging Checklist
  • Companion resource: Debugging Checklist
  • Companion resource: Variable Tracing Worksheet
  • Companion resource: Traceback Reading Guide
  • Next chapter: Defining and Calling Functions (Chapter 11, Part III)
Chapter 10 Checklist
Use this to confirm your understanding before moving to Chapter 11
Concepts
  • I can trace a program step by step and track variable values as they change.
  • I can build a tracing table for a program that includes a loop.
  • I can read a Python traceback and identify the file, line number, error type, and message.
  • I can identify a syntax error and explain why Python refuses to run the program.
  • I can identify a runtime error such as ZeroDivisionError, ValueError, or NameError.
  • I can recognize a logic error and explain why Python cannot detect it automatically.
Skills
  • I can use print-style debugging to observe variable values while a program runs.
  • I can write test cases that pair specific inputs with expected outputs.
  • I can identify edge cases for a problem, such as empty lists or negative numbers.
  • I can build a program incrementally, testing each new piece before adding more.
Readiness for Chapter 11
  • I am comfortable tracing conditionals and loops without getting lost.
  • I understand that a correct-looking program can still contain a hidden logic error.
  • I am ready to learn how functions organize and reuse blocks of code.