Tracing code by hand and with print
20 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)A wrong answer does not tell you where the bug is. Tracing does: you follow the program step by step and compare each value with your plan. This lesson shows how to do it on paper and with print.
Trace a loop with a table
Tracing is following a program one line at a time and writing down the value of every variable after each step, either on paper or with print calls.
A function should add up only the orders above a limit, but it gives a wrong total. Do not guess. Follow it one line at a time and write each variable down after every step.
def total_above(amounts, limit): total = 0 for a in amounts: if a > limit: total += a return total
print(total_above([120, 50, 300], 100))Example 1 · A loop that skips the first mark
marks = [40, 25, 35]total = 0for i in range(1, len(marks)): total += marks[i] print(f"{i=} {marks[i]=} {total=}")print("average", total / len(marks))i=1 marks[i]=25 total=25 i=2 marks[i]=35 total=60 average 20.0
What this code does
- The trace starts at
i=1, somarks[0]never reaches the total. - The fix is
for mark in marks, which visits every item.
Each row of the table holds one pass of the loop: a, the test a > limit, and total afterwards. The first wrong value tells you which line to look at.
Print the state you cannot see
Paper tracing is slow for long loops. Let Python write the table for you. An f-string with = prints the name and the value together.
total = 0for i, a in enumerate([120, 50, 300]): if a > 100: total += a print(f"{i=} {a=} {total=}")Example 2 · Trace Euclid's gcd
a, b = 48, 18while b: print(f"{a=} {b=}") a, b = b, a % bprint("gcd", a)a=48 b=18 a=18 b=12 a=12 b=6 gcd 6
What this code does
- Each line shows the pair before the step. Watch
bshrink until it reaches 0. a, b = b, a % bbuilds the right side first, so both names change together.
f"{total=}" prints total=420, so you never mix up which number is which. Put the print where the value changes.
Find the first wrong line
A long program has many steps. Print the result of each stage, and look for the first place where the value is not what you expected. The bug is at or just before that line.
orders = ["b-20", "a-35", "c-10"]amounts = [int(o.split("-")[1]) for o in orders]print(amounts)big = [x for x in amounts if x > 15]print(big)print(sum(big))Example 3 · One bubble pass, printed
items = [4, 2, 5, 1]for i in range(len(items) - 1): if items[i] > items[i + 1]: items[i], items[i + 1] = items[i + 1], items[i] print(i, items)0 [2, 4, 5, 1] 1 [2, 4, 5, 1] 2 [2, 4, 1, 5]
What this code does
- The list is printed after every comparison, so you can see the large value move right.
- After one pass the largest value, 5, is in its last place.
Three small prints split one pipeline into checkable steps. If amounts is right and big is wrong, the filter is the problem.
Keep debug output out of the answer
Judges compare only standard output. A leftover debug line in the answer makes a correct program fail. Send debug text to standard error, and delete it when you are done.
import sys
n = int(input())print(f"{n=}", file=sys.stderr)print(n * 2)Example 4 · Debug text on stderr
import sys
prices = [30, 70, 20]best = 0for p in prices: best = max(best, p) print(f"{p=} {best=}", file=sys.stderr)print(best)70
What this code does
- The checker reads standard output only, so the answer is the one line it sees.
- In a terminal both streams show up, which is why this is safe while you debug.
file=sys.stderr writes to the error stream, so the answer on standard output stays clean.
Next step
Check your understanding
10 quick questions