Errors and debugging in Python: read the message, then trace
Code that crashes is doing you a favor: it tells you where it broke. The harder bugs are the ones that run cleanly and print the wrong number.
The big idea
Debugging is a method: read the error's type and message, find the line, and inspect the actual values until one surprises you.
See it in code
When Python stops, the last line of the traceback names the error type and what went wrong. Here three classic mistakes are each caught with try/except, so you can read their messages side by side:
stock = {"TEE-SAGE-M": 12}
try:
print(profit * 2)
except NameError as error:
print("NameError:", error)
try:
print("2" * 38.0)
except TypeError as error:
print("TypeError:", error)
try:
print(stock["tee-sage-m"])
except KeyError as error:
print("KeyError:", error)NameError: name 'profit' is not defined TypeError: can't multiply sequence by non-int of type 'float' KeyError: 'tee-sage-m'
NameError means a name doesn't exist yet. TypeError means an operation got the wrong kind of value, here text where a number was needed. KeyError means a dictionary key isn't there, often because of capitalization.
Some bugs raise nothing. This forecast runs without complaint and reports a year-one figure that's far too low:
price = 38
units = 300
growth = 0.15
total_revenue = 0
for month in range(1, 13):
revenue = units * price
total_revenue = revenue
units = round(units * (1 + growth))
print(f"Year one revenue: ${total_revenue:,}")Year one revenue: $53,200
$53,200 for a year, when month one alone earns $11,400? A sanity check against a number you can estimate is the first debugging tool, before you read a line of code.
Now trace it. Print the suspect variable inside the loop for the first few passes, and look for the moment it stops behaving:
price = 38
units = 300
growth = 0.15
total_revenue = 0
for month in range(1, 13):
revenue = units * price
total_revenue = revenue
if month <= 3:
print(f"month {month}: revenue {revenue:,}, total so far {total_revenue:,}")
units = round(units * (1 + growth))
print(f"Year one revenue: ${total_revenue:,}")month 1: revenue 11,400, total so far 11,400 month 2: revenue 13,110, total so far 13,110 month 3: revenue 15,086, total so far 15,086 Year one revenue: $53,200
The total equals each month's revenue, so it isn't accumulating. The fix is one token: total_revenue = total_revenue + revenue. Then delete the trace print.
The method transfers to anything. A wrong protein, a strategy that never trades, a page that won't update: state what you expected, print what you got, and narrow the gap.
Try it yourself
Apply the fix and confirm year one becomes $331,398. Then break it a different way, by moving total_revenue = 0 inside the loop, and use the same trace to diagnose it.
The common mistake
Changing several things at once and re-running. If the bug vanishes you won't know why, and if it doesn't you've added noise. Form one hypothesis, make one change, observe.
What it unlocks
Understanding types prevents most TypeErrors before they happen. For errors you expect and want to survive, such as bad user input, use try / except.