Skip to content

Module 8: Error Handling and Debugging

Making Code More Robust — Handling Exceptions Gracefully


Module Overview

Errors are inevitable when programming. Files don't exist, data formats are incorrect, network requests fail... How can we make programs handle errors gracefully instead of crashing? This module teaches you Python's exception handling mechanisms and how to debug code to find and fix bugs.

Important Note: Beginners often fear errors, but error messages are your best teacher! Learn to read error messages, and you'll master the key to rapid debugging.


Learning Objectives

After completing this module, you will be able to:

  • Understand common Python error types
  • Use try-except to catch and handle exceptions
  • Write robust file reading and data processing code
  • Use finally and else clauses
  • Create custom exception classes
  • Master debugging techniques (print, breakpoints, pdb)
  • Read and understand error traceback information

Module Contents

01 - Error Types

Core Question: What are Python's common errors?

Core Content:

  • Syntax Errors vs Runtime Exceptions:
    • Syntax errors: Code is written incorrectly and cannot run (indentation errors, missing colons)
    • Runtime exceptions: Code can run but errors occur during execution
  • Common Runtime Exceptions:
    • NameError: Undefined variable
      python
      print(age)  # NameError: name 'age' is not defined
    • TypeError: Type error
      python
      result = "25" + 5  # TypeError: can only concatenate str
    • ValueError: Value error
      python
      int("abc")  # ValueError: invalid literal for int()
    • KeyError: Dictionary key doesn't exist
      python
      data = {'age': 25}
      print(data['income'])  # KeyError: 'income'
    • IndexError: Index out of range
      python
      list = [1, 2, 3]
      print(list[10])  # IndexError: list index out of range
    • FileNotFoundError: File doesn't exist
      python
      with open('missing.txt', 'r') as f:  # FileNotFoundError
          content = f.read()
    • ZeroDivisionError: Division by zero
      python
      result = 10 / 0  # ZeroDivisionError: division by zero

Practical Application:

python
# Common errors in data analysis
import pandas as pd

# 1. File doesn't exist
df = pd.read_csv('data.csv')  # FileNotFoundError

# 2. Column name error
mean_income = df['income'].mean()  # KeyError: 'income'

# 3. Type conversion error
age = int(df['age'][0])  # ValueError: invalid literal

02 - Try-Except Exception Handling

Core Question: How to catch and handle errors?

Core Content:

  • Basic Syntax:
    python
    try:
        # Code that might error
        result = 10 / 0
    except ZeroDivisionError:
        # Error handling
        print("Cannot divide by zero!")
  • Catching Multiple Exceptions:
    python
    try:
        value = int(input("Enter a number: "))
        result = 10 / value
    except ValueError:
        print("Input is not a number")
    except ZeroDivisionError:
        print("Cannot divide by zero")
    except Exception as e:
        print(f"Other error: {e}")
  • else and finally Clauses:
    python
    try:
        file = open('data.txt', 'r')
        content = file.read()
    except FileNotFoundError:
        print("File doesn't exist")
    else:
        print("File read successfully")  # Executes if no error
    finally:
        file.close()  # Always executes
  • Raising Exceptions:
    python
    def calculate_income(hours, rate):
        if hours < 0:
            raise ValueError("Hours cannot be negative")
        return hours * rate

Practical Application:

python
# Robust data reading
import pandas as pd

def safe_read_csv(file_path, default_value=None):
    """Safely read CSV file"""
    try:
        df = pd.read_csv(file_path)
        print(f"Successfully read {len(df)} rows")
        return df
    except FileNotFoundError:
        print(f"Error: File {file_path} doesn't exist")
        return default_value
    except pd.errors.EmptyDataError:
        print(f"Error: File {file_path} is empty")
        return default_value
    except Exception as e:
        print(f"Unknown error: {e}")
        return default_value

# Usage
df = safe_read_csv('survey.csv')
if df is not None:
    # Continue analysis...
    pass

03 - Debugging Tips

Core Question: How to find and fix bugs?

Core Content:

  • Reading Error Traceback:
    python
    Traceback (most recent call last):
      File "script.py", line 15, in <module>
        result = calculate_mean(data)
      File "script.py", line 8, in calculate_mean
        return sum(data) / len(data)
    ZeroDivisionError: division by zero
    • Read from bottom to top: last line is the error type
    • Middle shows the call stack: which file, which line, which function
  • Print Debugging:
    python
    def process_data(data):
        print(f"[DEBUG] Input data: {data}")  # Check input
        cleaned = [x for x in data if x > 0]
        print(f"[DEBUG] After cleaning: {cleaned}")  # Check intermediate result
        return sum(cleaned) / len(cleaned)
  • Using pdb Debugger:
    python
    import pdb
    
    def calculate_mean(data):
        pdb.set_trace()  # Breakpoint
        return sum(data) / len(data)
  • Jupyter Magic Commands:
    python
    %debug  # Enter debug mode for most recent error
    %%time  # Measure code execution time
    %pdb on  # Automatically enter debug mode
  • Common Debugging Strategies:
    • Binary search: Comment out half the code, find error section
    • Simplify input: Test with simplest data
    • Unit tests: Write small test cases

Practical Application:

python
# Case: Debugging data cleaning function
import pandas as pd

def clean_survey_data(df):
    """Clean survey data"""
    print(f"[DEBUG] Original data: {len(df)} rows, {len(df.columns)} columns")

    # Remove missing values
    df_clean = df.dropna(subset=['age', 'income'])
    print(f"[DEBUG] After removing NaN: {len(df_clean)} rows")

    # Filter ages
    df_clean = df_clean[(df_clean['age'] >= 18) & (df_clean['age'] <= 100)]
    print(f"[DEBUG] After age filtering: {len(df_clean)} rows")

    # Remove anomalous income
    df_clean = df_clean[df_clean['income'] > 0]
    print(f"[DEBUG] After removing anomalous income: {len(df_clean)} rows")

    return df_clean

Error Handling Strategy Comparison

StrategyAdvantagesDisadvantagesUse Cases
No HandlingSimpleProgram crashesExploratory analysis, Jupyter
try-exceptRobustCode verboseProduction code, batch processing
Default ValueElegantMay hide issuesOptional parameters, config reading
LoggingTraceableRequires extra codeProduction environment, long-term projects

How to Learn This Module?

Learning Path

Day 1 (2 hours): Error Types

  • Read 01 - Error Types
  • Recognize common errors
  • Learn to read error messages

Day 2 (3 hours): Try-Except

  • Read 02 - Try-Except
  • Practice catching exceptions
  • Write robust file reading code

Day 3 (2 hours): Debugging Tips

  • Read 03 - Debugging Tips
  • Use print debugging
  • Try pdb debugger

Total Time: 7 hours (1 week)

Minimized Learning Path

For beginners, priority is as follows:

Must Learn (Foundation skills, 4 hours):

  • 01 - Error Types (recognize common errors)
  • 02 - Try-Except (basic syntax)
  • Reading error messages techniques

Important (Advanced skills, 3 hours):

  • else and finally clauses
  • Print debugging
  • Jupyter debugging techniques

Optional (Advanced topics):

  • pdb debugger
  • Custom exception classes
  • Logging (logging module)

Learning Suggestions

  1. Don't Fear Errors

    • Errors are learning opportunities
    • Each error teaches you a new concept
    • Professional programmers encounter many errors too
  2. Learn to Read Error Messages

    python
    # Error message example
    Traceback (most recent call last):
      File "script.py", line 15, in <module>  # ← Start reading here
        result = calculate_mean(data)
      File "script.py", line 8, in calculate_mean
        return sum(data) / len(data)  # ← This line has error
    ZeroDivisionError: division by zero  # ← This is the error type
    
    # Interpretation:
    # 1. Error type: ZeroDivisionError
    # 2. Error location: script.py line 8
    # 3. Reason: Division by zero (len(data) = 0)
  3. Progressive Error Handling

    python
    # Stage 1: No handling (exploratory analysis)
    df = pd.read_csv('data.csv')
    
    # Stage 2: Simple handling (scripting)
    try:
        df = pd.read_csv('data.csv')
    except FileNotFoundError:
        print("File doesn't exist")
    
    # Stage 3: Complete handling (production code)
    def safe_read_csv(file_path):
        try:
            df = pd.read_csv(file_path)
            logger.info(f"Successfully read {len(df)} rows")
            return df
        except FileNotFoundError:
            logger.error(f"File doesn't exist: {file_path}")
            return None
        except Exception as e:
            logger.error(f"Unknown error: {e}")
            raise
  4. Error Handling Patterns for Common Scenarios

    python
    # Pattern 1: File reading
    try:
        df = pd.read_csv('data.csv')
    except FileNotFoundError:
        df = pd.DataFrame()  # Return empty DataFrame
    
    # Pattern 2: Dictionary access
    value = data.get('key', default_value)  # More elegant
    # Rather than
    try:
        value = data['key']
    except KeyError:
        value = default_value
    
    # Pattern 3: Type conversion
    try:
        age = int(user_input)
    except ValueError:
        age = None  # Or prompt user again

Common Questions

Q: When should I use try-except? A:

  • Must use: File operations, network requests, user input, type conversions
  • Can use: Data validation, optional features
  • Not recommended: Logic errors, algorithm bugs (should fix, not hide)

Q: Why not recommend using except Exception? A: Too broad, will catch all exceptions (including ones you don't want to catch). Should specify exception types.

python
#  Not recommended
try:
    result = 10 / x
except Exception:  # Too broad
    pass

#  Recommended
try:
    result = 10 / x
except ZeroDivisionError:  # Specific
    result = 0

Q: How to debug in Jupyter? A:

python
# Method 1: After cell throws error
%debug  # Enter debug mode

# Method 2: Automatic debugging
%pdb on  # All errors after this auto-enter debug

# Method 3: Set breakpoint in code
import pdb; pdb.set_trace()

Q: Print debugging vs pdb debugging? A:

  • Print: Simple, fast, suitable for beginners
  • pdb: Powerful, interactive, suitable for complex bugs

Q: Will error handling slow down code? A:

  • try block has almost no performance overhead
  • Only slows when exceptions are raised
  • Don't use exceptions to control normal flow

Next Steps

After completing this module, you will have mastered:

  • Recognizing and handling common Python errors
  • Using try-except to write robust code
  • Mastering debugging techniques to quickly locate bugs
  • Reading error traceback information

In Module 9, we'll dive into Data Science Core Libraries (NumPy, Pandas, Matplotlib), core skills for data analysis!

In Module 10, we'll learn Machine Learning and LLM APIs, exploring cutting-edge Python applications.

Errors aren't scary—learning to handle and debug is key! Keep going!


Released under the MIT License. Content © Author.