Python Errors and Debugging
Every programmer, beginner or expert, writes code with mistakes. The skill is not avoiding them but finding and fixing them quickly. Python helps a lot: when a program fails, it prints a traceback that says exactly where and why. This topic shows how to read tracebacks, what the most common errors mean, and how to hunt down bugs that do not produce an error at all.
Reading a traceback
def average(numbers):
return sum(numbers) / len(numbers)
def report(name, marks):
print(f"{name}: {average(marks):.1f}")
report("Asha", [90, 85])
report("Ravi", [])Asha: 87.5
Traceback (most recent call last):
File "main.py", line 8, in <module>
report("Ravi", [])
~~~~~~^^^^^^^^^^^^
File "main.py", line 5, in report
print(f"{name}: {average(marks):.1f}")
~~~~~~~^^^^^^^
File "main.py", line 2, in average
return sum(numbers) / len(numbers)
~~~~~~~~~~~~~^~~~~~~~~~~~~~
ZeroDivisionError: division by zeroRead a traceback from the bottom up. The last line names the error and gives its message: dividing by zero. The lines above show the chain of calls that led there, the most recent last: line 2 in average was called from line 5 in report, which was called from line 8. The markers under each line point at the exact part that failed. The fix belongs where the empty list should be handled, for example by returning 0 when numbers is empty.
Common errors
| Error | Usual cause |
|---|---|
SyntaxError | a missing colon, bracket or quote; Python cannot read the line |
IndentationError | the spaces at the start of the lines do not match |
NameError | a misspelt name, or a variable used before it gets a value |
TypeError | the wrong kind of value, such as adding text and a number |
ValueError | the right kind but a bad value, such as int("ten") |
IndexError | a list index past the end of the list |
KeyError | a dictionary key that does not exist |
AttributeError | a method or attribute this kind of value does not have |
ZeroDivisionError | dividing by zero |
In our editor, each error also gets a short hint in plain words, and the line is marked in the code.
Bugs without errors
The hardest bugs give no error: the program runs but prints the wrong answer. The oldest technique still works best: print the values along the way and compare them with what you expect. f-strings with = show the expression too:
prices = [100, 250, 75]
total = 0
for p in prices:
total = p # the bug: should be total += p
print(f"{p=} {total=}")
print("Total:", total)p=100 total=100 p=250 total=250 p=75 total=75 Total: 75
The printed values show at once that total is replaced instead of growing.
assert and small tests
assert condition, message stops the program with an AssertionError when the condition is false. Writing a few asserts for a function, with inputs whose answers you know, catches mistakes the moment you change the code:
def average(numbers):
return sum(numbers) / len(numbers) if numbers else 0
assert average([90, 85]) == 87.5
assert average([]) == 0
assert average([7]) == 7
print("all tests passed")all tests passed
Practice exercises
- Run the first example, read the traceback from the bottom up, and fix
averageso that an empty list gives 0. - Make each error from the table happen once on purpose, and read its message and hint.
- Find the bug in a loop that should count the even numbers in a list, using print.
- Write three asserts for a function that converts Celsius to Fahrenheit.