Types of Errors
- Syntax Errors: Problems with the structure of your code that prevent it from running at all (e.g., a missing colon, incorrect indentation). The interpreter will catch these before execution.
- Runtime Errors (Exceptions): Errors that occur while the program is running (e.g., trying to divide by zero, accessing a file that doesn't exist). These cause the program to crash if not handled.
- Logical Errors: The most devious kind. The code runs without crashing, but it produces the wrong result. The logic of the program is flawed.
1. Debugging with print()
The simplest and most universal debugging tool. When you're not sure what a variable's value is at a certain point, just print it!
Python
def calculate_average(numbers):
    total = 0
    print(f"Initial total: {total}") # Debug print
    for num in numbers:
        total = total + num
        print(f"Added {num}, new total: {total}") # Debug print
    
    # Oops, a logical error here!
    average = total / len(numbers) + 1 # This should not have '+ 1'
    print(f"Final average calculation: {average}") # Debug print
    return average
nums = [10, 20, 30]
calculate_average(nums)
While effective for simple cases, print() can clutter your code and becomes cumbersome for complex bugs.
2. The Python Debugger (pdb)
pdb is a powerful, interactive debugger built into the Python standard library. It allows you to pause your program's execution at any point and inspect the state (variables, etc.).
To use it, you insert a "breakpoint" in your code where you want to pause.
Python
import pdb
def process_data(data):
    # Let's set a breakpoint here to inspect 'data'
    pdb.set_trace()
    # Program execution will pause at this line
    
    processed_value = data * 2
    return processed_value
result = process_data(10)
print(result)
When you run this script, it will stop at pdb.set_trace() and give you a (Pdb) prompt in your terminal. Here are some essential commands:
- n (next): Execute the next line.
- c (continue): Continue execution until the next breakpoint or the end of the program.
- l (list): Show the source code around the current line.
- p <variable_name> (print): Print the value of a variable. For example, p data.
- q (quit): Exit the debugger and terminate the program.
pdb gives you a live, interactive look inside your program, which is far more powerful than static print statements.
3. Profiling: Finding Performance Bottlenecks
Debugging fixes correctness, while profiling fixes speed. A profiler is a tool that analyzes your code to see how much time and memory each part consumes. This helps you identify "bottlenecks"—the slow parts of your code that are worth optimizing.
Python includes a profiler called cProfile. You can run it from the command line.
Imagine you have a script my_slow_app.py. To profile it, you would run:
Bash
python -m cProfile -s time my_slow_app.py
- -m cProfile: Runs the cProfile module.
- -s time: Sorts the output by the cumulative time spent in each function.
The output will be a table showing how many times each function was called (ncalls) and the total time spent in it (tottime), helping you pinpoint exactly where your program is spending most of its time.