Faysal Ahmed
Chapter 9

Error Handling

Exceptions

Exceptions are errors detected during execution. Common built-in exceptions:

ExceptionMeaning
SyntaxErrorInvalid Python syntax
NameErrorVariable not defined
TypeErrorOperation on wrong type
ValueErrorValue is inappropriate
IndexErrorList index out of range
KeyErrorDictionary key not found
FileNotFoundErrorFile doesn’t exist
ZeroDivisionErrorDivision by zero
AttributeErrorAttribute doesn’t exist on object

try / except

try:
    number = int(input("Enter a number: "))
    result = 10 / number
    print(f"Result: {result}")
except ValueError:
    print("That's not a valid number!")
except ZeroDivisionError:
    print("Cannot divide by zero!")

Catching Multiple Exceptions

try:
    value = risky_operation()
except (ValueError, TypeError, RuntimeError) as e:
    print(f"Caught: {e}")

try / except / else / finally

try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("File not found.")
else:
    print(f"Read {len(content)} characters.")
    # else runs only if no exception occurred
finally:
    file.close()
    # finally always runs — even if return/break occurs

Raising Exceptions

def withdraw(balance, amount):
    if amount < 0:
        raise ValueError("Amount cannot be negative")
    if amount > balance:
        raise ValueError("Insufficient funds")
    return balance - amount

Creating Custom Exceptions

class InsufficientFundsError(Exception):
    """Raised when account balance is too low."""
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(f"Insufficient funds: ${balance} < ${amount}")

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

The Assert Statement

def divide(a, b):
    assert b != 0, "Division by zero!"
    return a / b

Asserts are for debugging. They can be disabled with -O flag.

Debugging Techniques

print(f"DEBUG: x={x}, y={y}")  # quick and effective

Using pdb

import pdb

def buggy_function():
    x = 10
    pdb.set_trace()   # execution pauses here — interactive debugger
    y = x / 0

Common Debugger Commands

CommandAction
nNext line
sStep into function
cContinue until next breakpoint
p varPrint variable value
lShow source code context
qQuit debugger

Next: Chapter 10 — Working with Libraries & Next Steps