Your program divides user input. If they enter zero, it crashes. Try-except lets you catch the error and respond gracefully - show a message, ask again, or use a default value. Your program keeps running.

Basic try-except

Catch and handle an exception.

basic_division.py
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()
# Basic try-except for division by zero

def main():
    # Without exception handling (crashes)
    print("Without try-except:")
    # This would crash: result = 10 / 0
    
    # With exception handling
    print("\nWith try-except:")
    try:
        result = 10 / 0
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("Error: Cannot divide by zero")
        result = None
    
    print(f"Program continues, result = {result}")
    
    
    # Safe division function
    print("\nSafe division:")
    numerator = 
    denominator = 
    
    try:
        answer = numerator / denominator
        print(f"{numerator} / {denominator} = {answer}")
    except ZeroDivisionError:
        print("Cannot divide by zero")
        answer = 0
    
    # Another example with zero
    print("\nWith zero denominator:")
    a = 15
    b = 0
    
    try:
        quotient = a / b
        print(f"{a} / {b} = {quotient}")
    except ZeroDivisionError:
        print(f"Cannot divide {a} by zero, using default value")
        quotient = float('inf')  # infinity
    
    print(f"Final quotient: {quotient}")
    
if __name__ == "__main__":
    main()

Code that might fail goes in try. Error handling goes in except.

try-except `try: risky code except ExceptionType: handle it`. Prevents crashes.

Catch index errors

Handle accessing invalid list indices.

index_error.py
# Catching IndexError when accessing list elements

def main():
    # Safe list access
    scores = 
    
    print("Scores:", scores)
    print(f"Total scores: {len(scores)}")
    
    # Access valid index
    try:
        first = scores[0]
        print(f"\nFirst score: {first}")
    except IndexError:
        print("Index out of range")
        first = None
    
    # Access invalid index
    print("\nAccessing index 10:")
    try:
        value = scores[10]
        print(f"Value: {value}")
    except IndexError:
        print("Error: Index 10 doesn't exist")
        value = None
    
    
    # Safe get function
    def safe_get(lst, index, default=0):
        try:
            return lst[index]
        except IndexError:
            return default
    
    print("\nUsing safe_get:")
    print(f"Index 2: {safe_get(scores, 2)}")
    print(f"Index 99: {safe_get(scores, 99, default=-1)}")
    
    # Multiple attempts
    indices = [0, 2, 5, 3, 10]
    print("\nAccessing multiple indices:")
    for i in indices:
        try:
            score = scores[i]
            print(f"  scores[{i}] = {score}")
        except IndexError:
            print(f"  scores[{i}] = OUT OF RANGE")
    
if __name__ == "__main__":
    main()
# Catching IndexError when accessing list elements

def main():
    # Safe list access
    scores = 
    
    print("Scores:", scores)
    print(f"Total scores: {len(scores)}")
    
    # Access valid index
    try:
        first = scores[0]
        print(f"\nFirst score: {first}")
    except IndexError:
        print("Index out of range")
        first = None
    
    # Access invalid index
    print("\nAccessing index 10:")
    try:
        value = scores[10]
        print(f"Value: {value}")
    except IndexError:
        print("Error: Index 10 doesn't exist")
        value = None
    
    
    # Safe get function
    def safe_get(lst, index, default=0):
        try:
            return lst[index]
        except IndexError:
            return default
    
    print("\nUsing safe_get:")
    print(f"Index 2: {safe_get(scores, 2)}")
    print(f"Index 99: {safe_get(scores, 99, default=-1)}")
    
    # Multiple attempts
    indices = [0, 2, 5, 3, 10]
    print("\nAccessing multiple indices:")
    for i in indices:
        try:
            score = scores[i]
            print(f"  scores[{i}] = {score}")
        except IndexError:
            print(f"  scores[{i}] = OUT OF RANGE")
    
if __name__ == "__main__":
    main()
# Catching IndexError when accessing list elements

def main():
    # Safe list access
    scores = 
    
    print("Scores:", scores)
    print(f"Total scores: {len(scores)}")
    
    # Access valid index
    try:
        first = scores[0]
        print(f"\nFirst score: {first}")
    except IndexError:
        print("Index out of range")
        first = None
    
    # Access invalid index
    print("\nAccessing index 10:")
    try:
        value = scores[10]
        print(f"Value: {value}")
    except IndexError:
        print("Error: Index 10 doesn't exist")
        value = None
    
    
    # Safe get function
    def safe_get(lst, index, default=0):
        try:
            return lst[index]
        except IndexError:
            return default
    
    print("\nUsing safe_get:")
    print(f"Index 2: {safe_get(scores, 2)}")
    print(f"Index 99: {safe_get(scores, 99, default=-1)}")
    
    # Multiple attempts
    indices = [0, 2, 5, 3, 10]
    print("\nAccessing multiple indices:")
    for i in indices:
        try:
            score = scores[i]
            print(f"  scores[{i}] = {score}")
        except IndexError:
            print(f"  scores[{i}] = OUT OF RANGE")
    
if __name__ == "__main__":
    main()

IndexError when accessing index outside list range.

Multiple except blocks

Handle different exceptions differently.

multiple_except.py
# Multiple except blocks for different errors

def main():
    # Parse user input (multiple error types)
    print("Input parsing with multiple exceptions:\n")
    
    data = {
        "name": "Alice",
        "age": "25",
        "scores": [85, 90, 78]
    }
    
    # Catch ValueError (bad conversion)
    try:
        age_str = 
        age = int(age_str)
        print(f"Age: {age}")
    except ValueError:
        print("ValueError: Cannot convert to integer")
        age = 0
    
    # Catch KeyError (missing key)
    try:
        email = data["email"]  # key doesn't exist
        print(f"Email: {email}")
    except KeyError:
        print("KeyError: 'email' key not found")
        email = "unknown@example.com"
    
    
    # Multiple exceptions in one try block
    print("\nProcessing with multiple potential errors:")
    
    try:
        # Could raise KeyError
        score_str = data["score"]  # missing key
        # Could raise ValueError
        score = int(score_str)
        # Could raise IndexError
        first_score = data["scores"][10]
        
        print(f"Score: {score}, First: {first_score}")
        
    except KeyError as e:
        print(f"Missing key: {e}")
    except ValueError as e:
        print(f"Invalid value: {e}")
    except IndexError as e:
        print(f"Index error: {e}")
    
    # Catch parent exception
    print("\nUsing generic Exception:")
    
    try:
        result = data["count"] / 0
    except ZeroDivisionError:
        print("Specific: Division by zero")
    except Exception as e:
        print(f"Generic: {type(e).__name__}: {e}")
    
    # Process list of operations
    operations = [
        ("parse", "123"),
        ("parse", "abc"),
        ("divide", 10, 0),
        ("access", [1, 2], 5)
    ]
    
    print("\nProcessing operations:")
    for op in operations:
        try:
            if op[0] == "parse":
                result = int(op[1])
                print(f"  Parsed: {result}")
            elif op[0] == "divide":
                result = op[1] / op[2]
                print(f"  Divided: {result}")
            elif op[0] == "access":
                result = op[1][op[2]]
                print(f"  Accessed: {result}")
        except ValueError:
            print(f"  ValueError in {op}")
        except ZeroDivisionError:
            print(f"  ZeroDivisionError in {op}")
        except IndexError:
            print(f"  IndexError in {op}")
    
if __name__ == "__main__":
    main()
# Multiple except blocks for different errors

def main():
    # Parse user input (multiple error types)
    print("Input parsing with multiple exceptions:\n")
    
    data = {
        "name": "Alice",
        "age": "25",
        "scores": [85, 90, 78]
    }
    
    # Catch ValueError (bad conversion)
    try:
        age_str = 
        age = int(age_str)
        print(f"Age: {age}")
    except ValueError:
        print("ValueError: Cannot convert to integer")
        age = 0
    
    # Catch KeyError (missing key)
    try:
        email = data["email"]  # key doesn't exist
        print(f"Email: {email}")
    except KeyError:
        print("KeyError: 'email' key not found")
        email = "unknown@example.com"
    
    
    # Multiple exceptions in one try block
    print("\nProcessing with multiple potential errors:")
    
    try:
        # Could raise KeyError
        score_str = data["score"]  # missing key
        # Could raise ValueError
        score = int(score_str)
        # Could raise IndexError
        first_score = data["scores"][10]
        
        print(f"Score: {score}, First: {first_score}")
        
    except KeyError as e:
        print(f"Missing key: {e}")
    except ValueError as e:
        print(f"Invalid value: {e}")
    except IndexError as e:
        print(f"Index error: {e}")
    
    # Catch parent exception
    print("\nUsing generic Exception:")
    
    try:
        result = data["count"] / 0
    except ZeroDivisionError:
        print("Specific: Division by zero")
    except Exception as e:
        print(f"Generic: {type(e).__name__}: {e}")
    
    # Process list of operations
    operations = [
        ("parse", "123"),
        ("parse", "abc"),
        ("divide", 10, 0),
        ("access", [1, 2], 5)
    ]
    
    print("\nProcessing operations:")
    for op in operations:
        try:
            if op[0] == "parse":
                result = int(op[1])
                print(f"  Parsed: {result}")
            elif op[0] == "divide":
                result = op[1] / op[2]
                print(f"  Divided: {result}")
            elif op[0] == "access":
                result = op[1][op[2]]
                print(f"  Accessed: {result}")
        except ValueError:
            print(f"  ValueError in {op}")
        except ZeroDivisionError:
            print(f"  ZeroDivisionError in {op}")
        except IndexError:
            print(f"  IndexError in {op}")
    
if __name__ == "__main__":
    main()
# Multiple except blocks for different errors

def main():
    # Parse user input (multiple error types)
    print("Input parsing with multiple exceptions:\n")
    
    data = {
        "name": "Alice",
        "age": "25",
        "scores": [85, 90, 78]
    }
    
    # Catch ValueError (bad conversion)
    try:
        age_str = 
        age = int(age_str)
        print(f"Age: {age}")
    except ValueError:
        print("ValueError: Cannot convert to integer")
        age = 0
    
    # Catch KeyError (missing key)
    try:
        email = data["email"]  # key doesn't exist
        print(f"Email: {email}")
    except KeyError:
        print("KeyError: 'email' key not found")
        email = "unknown@example.com"
    
    
    # Multiple exceptions in one try block
    print("\nProcessing with multiple potential errors:")
    
    try:
        # Could raise KeyError
        score_str = data["score"]  # missing key
        # Could raise ValueError
        score = int(score_str)
        # Could raise IndexError
        first_score = data["scores"][10]
        
        print(f"Score: {score}, First: {first_score}")
        
    except KeyError as e:
        print(f"Missing key: {e}")
    except ValueError as e:
        print(f"Invalid value: {e}")
    except IndexError as e:
        print(f"Index error: {e}")
    
    # Catch parent exception
    print("\nUsing generic Exception:")
    
    try:
        result = data["count"] / 0
    except ZeroDivisionError:
        print("Specific: Division by zero")
    except Exception as e:
        print(f"Generic: {type(e).__name__}: {e}")
    
    # Process list of operations
    operations = [
        ("parse", "123"),
        ("parse", "abc"),
        ("divide", 10, 0),
        ("access", [1, 2], 5)
    ]
    
    print("\nProcessing operations:")
    for op in operations:
        try:
            if op[0] == "parse":
                result = int(op[1])
                print(f"  Parsed: {result}")
            elif op[0] == "divide":
                result = op[1] / op[2]
                print(f"  Divided: {result}")
            elif op[0] == "access":
                result = op[1][op[2]]
                print(f"  Accessed: {result}")
        except ValueError:
            print(f"  ValueError in {op}")
        except ZeroDivisionError:
            print(f"  ZeroDivisionError in {op}")
        except IndexError:
            print(f"  IndexError in {op}")
    
if __name__ == "__main__":
    main()

Specific exceptions first, general Exception last. Order matters.

Catch parent exception

Catch parent type to handle all subtypes.

parent_exception.py
# Catching parent exception types

def main():
    # Catch general Exception
    print("Catching general Exception:\n")
    
    numbers = [10, 20, 30]
    
    try:
        # Could be any type of error
        index = 
        value = numbers[index]
        result = value / 0
    except Exception as e:
        # Catches ANY exception
        print(f"Caught: {type(e).__name__}")
        print(f"Message: {e}")
    
    
    # Specific vs general order matters
    print("\nException handling order:")
    
    data = {"x": "not_a_number"}
    
    try:
        num = int(data["y"])
    except KeyError:
        print("Specific: Missing key")
    except ValueError:
        print("Specific: Bad value")
    except Exception as e:
        print(f"General: {type(e).__name__}")
    
    # Access exception details
    print("\nException details:")
    
    items = [1, 2, 3]
    
    try:
        value = items[10]
    except Exception as e:
        print(f"Type: {type(e).__name__}")
        print(f"Message: {str(e)}")
        print(f"Args: {e.args}")
    
    # Multiple operations with general catch
    def safe_operate(operation, a, b):
        try:
            if operation == "divide":
                return a / b
            elif operation == "get":
                return a[b]
            elif operation == "parse":
                return int(a)
            else:
                return None
        except Exception as e:
            print(f"  Error in {operation}: {type(e).__name__}")
            return None
    
    print("\nSafe operations:")
    print(f"divide 10/2: {safe_operate('divide', 10, 2)}")
    print(f"divide 10/0: {safe_operate('divide', 10, 0)}")
    print(f"get [1,2,3][5]: {safe_operate('get', [1,2,3], 5)}")
    print(f"parse 'abc': {safe_operate('parse', 'abc', None)}")
    
    # Catch and re-raise pattern
    print("\nLogging then re-raising:")
    
    try:
        try:
            result = 100 / 0
        except ZeroDivisionError as e:
            print(f"Logging: {e}")
            # Could log to file here
            # Then let it propagate
    except Exception:
        print("Outer handler caught it")
    
if __name__ == "__main__":
    main()
# Catching parent exception types

def main():
    # Catch general Exception
    print("Catching general Exception:\n")
    
    numbers = [10, 20, 30]
    
    try:
        # Could be any type of error
        index = 
        value = numbers[index]
        result = value / 0
    except Exception as e:
        # Catches ANY exception
        print(f"Caught: {type(e).__name__}")
        print(f"Message: {e}")
    
    
    # Specific vs general order matters
    print("\nException handling order:")
    
    data = {"x": "not_a_number"}
    
    try:
        num = int(data["y"])
    except KeyError:
        print("Specific: Missing key")
    except ValueError:
        print("Specific: Bad value")
    except Exception as e:
        print(f"General: {type(e).__name__}")
    
    # Access exception details
    print("\nException details:")
    
    items = [1, 2, 3]
    
    try:
        value = items[10]
    except Exception as e:
        print(f"Type: {type(e).__name__}")
        print(f"Message: {str(e)}")
        print(f"Args: {e.args}")
    
    # Multiple operations with general catch
    def safe_operate(operation, a, b):
        try:
            if operation == "divide":
                return a / b
            elif operation == "get":
                return a[b]
            elif operation == "parse":
                return int(a)
            else:
                return None
        except Exception as e:
            print(f"  Error in {operation}: {type(e).__name__}")
            return None
    
    print("\nSafe operations:")
    print(f"divide 10/2: {safe_operate('divide', 10, 2)}")
    print(f"divide 10/0: {safe_operate('divide', 10, 0)}")
    print(f"get [1,2,3][5]: {safe_operate('get', [1,2,3], 5)}")
    print(f"parse 'abc': {safe_operate('parse', 'abc', None)}")
    
    # Catch and re-raise pattern
    print("\nLogging then re-raising:")
    
    try:
        try:
            result = 100 / 0
        except ZeroDivisionError as e:
            print(f"Logging: {e}")
            # Could log to file here
            # Then let it propagate
    except Exception:
        print("Outer handler caught it")
    
if __name__ == "__main__":
    main()
# Catching parent exception types

def main():
    # Catch general Exception
    print("Catching general Exception:\n")
    
    numbers = [10, 20, 30]
    
    try:
        # Could be any type of error
        index = 
        value = numbers[index]
        result = value / 0
    except Exception as e:
        # Catches ANY exception
        print(f"Caught: {type(e).__name__}")
        print(f"Message: {e}")
    
    
    # Specific vs general order matters
    print("\nException handling order:")
    
    data = {"x": "not_a_number"}
    
    try:
        num = int(data["y"])
    except KeyError:
        print("Specific: Missing key")
    except ValueError:
        print("Specific: Bad value")
    except Exception as e:
        print(f"General: {type(e).__name__}")
    
    # Access exception details
    print("\nException details:")
    
    items = [1, 2, 3]
    
    try:
        value = items[10]
    except Exception as e:
        print(f"Type: {type(e).__name__}")
        print(f"Message: {str(e)}")
        print(f"Args: {e.args}")
    
    # Multiple operations with general catch
    def safe_operate(operation, a, b):
        try:
            if operation == "divide":
                return a / b
            elif operation == "get":
                return a[b]
            elif operation == "parse":
                return int(a)
            else:
                return None
        except Exception as e:
            print(f"  Error in {operation}: {type(e).__name__}")
            return None
    
    print("\nSafe operations:")
    print(f"divide 10/2: {safe_operate('divide', 10, 2)}")
    print(f"divide 10/0: {safe_operate('divide', 10, 0)}")
    print(f"get [1,2,3][5]: {safe_operate('get', [1,2,3], 5)}")
    print(f"parse 'abc': {safe_operate('parse', 'abc', None)}")
    
    # Catch and re-raise pattern
    print("\nLogging then re-raising:")
    
    try:
        try:
            result = 100 / 0
        except ZeroDivisionError as e:
            print(f"Logging: {e}")
            # Could log to file here
            # Then let it propagate
    except Exception:
        print("Outer handler caught it")
    
if __name__ == "__main__":
    main()
# Catching parent exception types

def main():
    # Catch general Exception
    print("Catching general Exception:\n")
    
    numbers = [10, 20, 30]
    
    try:
        # Could be any type of error
        index = 
        value = numbers[index]
        result = value / 0
    except Exception as e:
        # Catches ANY exception
        print(f"Caught: {type(e).__name__}")
        print(f"Message: {e}")
    
    
    # Specific vs general order matters
    print("\nException handling order:")
    
    data = {"x": "not_a_number"}
    
    try:
        num = int(data["y"])
    except KeyError:
        print("Specific: Missing key")
    except ValueError:
        print("Specific: Bad value")
    except Exception as e:
        print(f"General: {type(e).__name__}")
    
    # Access exception details
    print("\nException details:")
    
    items = [1, 2, 3]
    
    try:
        value = items[10]
    except Exception as e:
        print(f"Type: {type(e).__name__}")
        print(f"Message: {str(e)}")
        print(f"Args: {e.args}")
    
    # Multiple operations with general catch
    def safe_operate(operation, a, b):
        try:
            if operation == "divide":
                return a / b
            elif operation == "get":
                return a[b]
            elif operation == "parse":
                return int(a)
            else:
                return None
        except Exception as e:
            print(f"  Error in {operation}: {type(e).__name__}")
            return None
    
    print("\nSafe operations:")
    print(f"divide 10/2: {safe_operate('divide', 10, 2)}")
    print(f"divide 10/0: {safe_operate('divide', 10, 0)}")
    print(f"get [1,2,3][5]: {safe_operate('get', [1,2,3], 5)}")
    print(f"parse 'abc': {safe_operate('parse', 'abc', None)}")
    
    # Catch and re-raise pattern
    print("\nLogging then re-raising:")
    
    try:
        try:
            result = 100 / 0
        except ZeroDivisionError as e:
            print(f"Logging: {e}")
            # Could log to file here
            # Then let it propagate
    except Exception:
        print("Outer handler caught it")
    
if __name__ == "__main__":
    main()

Catching Exception catches (almost) everything. Be specific when possible.

Finally block

Code that always runs, even after exception.

finally_block.py
# Finally block for cleanup

def main():
    # Finally always executes
    print("Finally block basics:\n")
    
    try:
        print("In try block")
        result = 10 / 2
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("In except block")
    finally:
        print("In finally block (always runs)")
    
    # Finally runs even with exception
    print("\nWith exception:")
    
    try:
        print("In try block")
        result = 10 / 0  # raises error
        print("This won't print")
    except ZeroDivisionError:
        print("In except block")
    finally:
        print("In finally block (runs despite error)")
    
    
    # Simulated file handling
    def process_file(filename):
        file_handle = None
        try:
            print(f"\nOpening {filename}")
            file_handle = f"<handle:{filename}>"
            
            if "error" in filename:
                raise ValueError("Simulated read error")
            
            print(f"Processing {filename}")
            return "SUCCESS"
            
        except ValueError as e:
            print(f"Error: {e}")
            return "FAILED"
        finally:
            if file_handle:
                print(f"Closing {file_handle}")
    
    result1 = process_file("data.txt")
    print(f"Result: {result1}")
    
    result2 = process_file("error.txt")
    print(f"Result: {result2}")
    
    # Try-except-else-finally (complete form)
    print("\nComplete form:")
    
    value = 
    
    try:
        num = int(value)
    except ValueError:
        print("Conversion failed")
        num = 0
    else:
        # Runs only if NO exception
        print(f"Conversion succeeded: {num}")
    finally:
        print("Cleanup complete")
    
    # Resource tracking
    class Resource:
        def __init__(self, name):
            self.name = name
            print(f"  Acquired: {name}")
        
        def close(self):
            print(f"  Released: {self.name}")
    
    print("\nResource management:")
    
    resource = None
    try:
        resource = Resource("Database")
        # Simulate work
        items = [1, 2, 3]
        value = items[5]  # Error!
    except IndexError:
        print("  Error during processing")
    finally:
        if resource:
            resource.close()
    
if __name__ == "__main__":
    main()
# Finally block for cleanup

def main():
    # Finally always executes
    print("Finally block basics:\n")
    
    try:
        print("In try block")
        result = 10 / 2
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("In except block")
    finally:
        print("In finally block (always runs)")
    
    # Finally runs even with exception
    print("\nWith exception:")
    
    try:
        print("In try block")
        result = 10 / 0  # raises error
        print("This won't print")
    except ZeroDivisionError:
        print("In except block")
    finally:
        print("In finally block (runs despite error)")
    
    
    # Simulated file handling
    def process_file(filename):
        file_handle = None
        try:
            print(f"\nOpening {filename}")
            file_handle = f"<handle:{filename}>"
            
            if "error" in filename:
                raise ValueError("Simulated read error")
            
            print(f"Processing {filename}")
            return "SUCCESS"
            
        except ValueError as e:
            print(f"Error: {e}")
            return "FAILED"
        finally:
            if file_handle:
                print(f"Closing {file_handle}")
    
    result1 = process_file("data.txt")
    print(f"Result: {result1}")
    
    result2 = process_file("error.txt")
    print(f"Result: {result2}")
    
    # Try-except-else-finally (complete form)
    print("\nComplete form:")
    
    value = 
    
    try:
        num = int(value)
    except ValueError:
        print("Conversion failed")
        num = 0
    else:
        # Runs only if NO exception
        print(f"Conversion succeeded: {num}")
    finally:
        print("Cleanup complete")
    
    # Resource tracking
    class Resource:
        def __init__(self, name):
            self.name = name
            print(f"  Acquired: {name}")
        
        def close(self):
            print(f"  Released: {self.name}")
    
    print("\nResource management:")
    
    resource = None
    try:
        resource = Resource("Database")
        # Simulate work
        items = [1, 2, 3]
        value = items[5]  # Error!
    except IndexError:
        print("  Error during processing")
    finally:
        if resource:
            resource.close()
    
if __name__ == "__main__":
    main()
# Finally block for cleanup

def main():
    # Finally always executes
    print("Finally block basics:\n")
    
    try:
        print("In try block")
        result = 10 / 2
        print(f"Result: {result}")
    except ZeroDivisionError:
        print("In except block")
    finally:
        print("In finally block (always runs)")
    
    # Finally runs even with exception
    print("\nWith exception:")
    
    try:
        print("In try block")
        result = 10 / 0  # raises error
        print("This won't print")
    except ZeroDivisionError:
        print("In except block")
    finally:
        print("In finally block (runs despite error)")
    
    
    # Simulated file handling
    def process_file(filename):
        file_handle = None
        try:
            print(f"\nOpening {filename}")
            file_handle = f"<handle:{filename}>"
            
            if "error" in filename:
                raise ValueError("Simulated read error")
            
            print(f"Processing {filename}")
            return "SUCCESS"
            
        except ValueError as e:
            print(f"Error: {e}")
            return "FAILED"
        finally:
            if file_handle:
                print(f"Closing {file_handle}")
    
    result1 = process_file("data.txt")
    print(f"Result: {result1}")
    
    result2 = process_file("error.txt")
    print(f"Result: {result2}")
    
    # Try-except-else-finally (complete form)
    print("\nComplete form:")
    
    value = 
    
    try:
        num = int(value)
    except ValueError:
        print("Conversion failed")
        num = 0
    else:
        # Runs only if NO exception
        print(f"Conversion succeeded: {num}")
    finally:
        print("Cleanup complete")
    
    # Resource tracking
    class Resource:
        def __init__(self, name):
            self.name = name
            print(f"  Acquired: {name}")
        
        def close(self):
            print(f"  Released: {self.name}")
    
    print("\nResource management:")
    
    resource = None
    try:
        resource = Resource("Database")
        # Simulate work
        items = [1, 2, 3]
        value = items[5]  # Error!
    except IndexError:
        print("  Error during processing")
    finally:
        if resource:
            resource.close()
    
if __name__ == "__main__":
    main()

finally: runs whether exception occurred or not. Good for cleanup.

finally Cleanup code that always runs. Close files, release resources.

Exercise: practical.py

Build a robust input parser with complete exception handling