🔍 Debugging Code
Debugging is the process of finding and fixing bugs in your code. Python provides several tools and techniques to help you identify issues and understand how your code behaves.
# Simple debugging with print statements
def calculate_average(numbers):
print(f"Input numbers: {numbers}") # Debug output
total = sum(numbers)
print(f"Total sum: {total}") # Debug output
average = total / len(numbers)
print(f"Average: {average}") # Debug output
return average
result = calculate_average([10, 20, 30])
print(f"Final result: {result}")
🎯 Basic Debugging Techniques
Start with simple debugging methods that require no special tools.
Print Statement Debugging
# Debug with print statements
def find_maximum(numbers):
print(f"Starting with numbers: {numbers}")
if not numbers:
print("List is empty!")
return None
max_value = numbers[0]
print(f"Initial max_value: {max_value}")
for i, num in enumerate(numbers[1:], 1):
print(f"Comparing {max_value} with {num} at index {i}")
if num > max_value:
max_value = num
print(f"New max_value: {max_value}")
print(f"Final maximum: {max_value}")
return max_value
# Test the function
result = find_maximum([3, 7, 2, 9, 1])
Variable Inspection
# Inspect variables at different points
def process_text(text):
print(f"Original text: '{text}'")
print(f"Text type: {type(text)}")
print(f"Text length: {len(text)}")
# Process the text
cleaned = text.strip().lower()
print(f"After cleaning: '{cleaned}'")
words = cleaned.split()
print(f"Words: {words}")
print(f"Word count: {len(words)}")
return words
words = process_text(" Hello World Python ")
Debug Function Calls
# Track function execution
def debug_function_call(func_name, *args, **kwargs):
print(f"Calling {func_name} with args={args}, kwargs={kwargs}")
def add_numbers(a, b):
debug_function_call("add_numbers", a, b)
result = a + b
print(f"add_numbers returning: {result}")
return result
def multiply_numbers(a, b):
debug_function_call("multiply_numbers", a, b)
result = a * b
print(f"multiply_numbers returning: {result}")
return result
# Test functions
sum_result = add_numbers(5, 3)
product_result = multiply_numbers(4, 6)
🐛 Python Debugger (pdb)
The Python debugger allows you to step through code line by line.
Basic Debugger Commands
Command | Shortcut | Purpose |
---|---|---|
continue | c | Continue execution |
step | s | Step into functions |
next | n | Next line (don't step into) |
list | l | Show current code |
print | p | Print variable value |
quit | q | Quit debugger |
Using the Debugger
import pdb
def buggy_function(numbers):
total = 0
for i in range(len(numbers)):
pdb.set_trace() # Start debugger here
total += numbers[i]
return total
# When this runs, debugger will activate
# Use 'n' to step through, 'p total' to print total
# Use 'c' to continue, 'q' to quit
result = buggy_function([1, 2, 3, 4])
Conditional Breakpoints
import pdb
def process_items(items):
for i, item in enumerate(items):
# Only break on specific condition
if item < 0:
pdb.set_trace() # Debug negative values
processed = item * 2
print(f"Item {i}: {item} -> {processed}")
# Test with mixed values
process_items([5, -2, 8, -1, 3])
📋 Debugging Strategies
Strategy | When to Use | Example |
---|---|---|
Print debugging | Simple issues | print(f"x = {x}") |
Rubber duck | Logic problems | Explain code to someone |
Binary search | Large codebase | Comment out half the code |
Minimal example | Complex bugs | Recreate with simple code |
🔧 Common Debugging Scenarios
Index and Range Errors
def safe_list_access(items, index):
print(f"Accessing index {index} in list of length {len(items)}")
print(f"List contents: {items}")
if index < 0:
print(f"Negative index: {index}")
return None
if index >= len(items):
print(f"Index {index} out of range (max: {len(items)-1})")
return None
value = items[index]
print(f"Found value: {value}")
return value
# Test edge cases
test_list = [10, 20, 30]
safe_list_access(test_list, 5) # Out of range
safe_list_access(test_list, -1) # Negative
safe_list_access(test_list, 1) # Valid
Type and Value Errors
def debug_calculations(a, b):
print(f"Input a: {a} (type: {type(a)})")
print(f"Input b: {b} (type: {type(b)})")
# Check if inputs are numbers
if not isinstance(a, (int, float)):
print(f"Error: 'a' is not a number")
return None
if not isinstance(b, (int, float)):
print(f"Error: 'b' is not a number")
return None
# Check for division by zero
if b == 0:
print("Error: Cannot divide by zero")
return None
result = a / b
print(f"Result: {a} / {b} = {result}")
return result
# Test with different inputs
debug_calculations(10, 2) # Valid
debug_calculations("10", 2) # Type error
debug_calculations(10, 0) # Division by zero
Logic Error Detection
def find_even_numbers(numbers):
even_numbers = []
print(f"Looking for even numbers in: {numbers}")
for i, num in enumerate(numbers):
print(f"Checking number {num} at index {i}")
if num % 2 == 0:
print(f" {num} is even - adding to list")
even_numbers.append(num)
else:
print(f" {num} is odd - skipping")
print(f"Final even numbers: {even_numbers}")
return even_numbers
# Test the function
result = find_even_numbers([1, 2, 3, 4, 5, 6])
📊 Debug Information Reference
Useful Built-in Functions for Debugging
Function | Purpose | Example |
---|---|---|
type() | Get object type | type(variable) |
len() | Get length | len(list) |
dir() | List object attributes | dir(object) |
vars() | Get object variables | vars(object) |
id() | Get object ID | id(variable) |
repr() | Get string representation | repr(object) |
Error Information
Attribute | Purpose | Example |
---|---|---|
__class__ | Exception class | e.__class__.__name__ |
args | Exception arguments | e.args |
__traceback__ | Traceback object | e.__traceback__ |
🎯 Key Takeaways
🚀 What's Next?
Learn how to implement proper logging for tracking application behavior and errors.
Continue to: Use Logging
Was this helpful?
Track Your Learning Progress
Sign in to bookmark tutorials and keep track of your learning journey.
Your progress is saved automatically as you read.