Lesson 17 of 25

Error Handling

Try, Except, and Finally

Errors (exceptions) are a normal part of programming. Instead of letting your program crash, you can use try/except blocks to catch errors and handle them gracefully.

The 'try' block contains code that might cause an error. If an error occurs, Python jumps to the matching 'except' block. The optional 'finally' block runs no matter what — whether an error occurred or not.

Example
# Basic try/except
try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero!")

# Catching the error object
try:
    number = int("hello")
except ValueError as e:
    print(f"Error: {e}")  # Error: invalid literal for int()...

# Multiple except blocks
try:
    my_list = [1, 2, 3]
    print(my_list[10])
except IndexError:
    print("Index out of range!")
except TypeError:
    print("Wrong type!")

# Try/except/else/finally
try:
    number = int("42")
except ValueError:
    print("Invalid number!")
else:
    print(f"Success! Number is {number}")  # Runs if no error
finally:
    print("This always runs.")  # Cleanup code

# Output:
# Success! Number is 42
# This always runs.
  • try: — wrap code that might raise an exception
  • except ExceptionType: — handle a specific exception
  • except ExceptionType as e: — capture the error message
  • except: — catch any exception (use sparingly)
  • else: — runs only if no exception occurred
  • finally: — always runs, regardless of exceptions (great for cleanup)
Try Error Handling
JavaScript
# Safe division function
def safe_divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        return "Cannot divide by zero!"
    else:
        return result

print(safe_divide(10, 3))
print(safe_divide(10, 0))

# Safe input conversion
def safe_int(value):
    try:
        return int(value)
    except ValueError:
        return f"'{value}' is not a valid number"

print(safe_int("42"))
print(safe_int("hello"))
Notes
  • Avoid bare 'except:' clauses that catch all exceptions. Be specific about which exceptions you expect. Catching all exceptions can hide bugs.

Raising Exceptions and Custom Exceptions

You can raise exceptions intentionally using the 'raise' keyword. This is useful for enforcing rules in your code — for example, validating that a function receives valid input.

You can also create custom exception classes by inheriting from Python's built-in Exception class.

Example
# Raising exceptions
def set_age(age):
    if not isinstance(age, int):
        raise TypeError("Age must be an integer")
    if age < 0 or age > 150:
        raise ValueError("Age must be between 0 and 150")
    return age

try:
    set_age(-5)
except ValueError as e:
    print(f"Validation error: {e}")
# Validation error: Age must be between 0 and 150

# Custom exception classes
class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(
            f"Cannot withdraw ${amount}. Balance: ${balance}"
        )

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

try:
    new_balance = withdraw(100, 150)
except InsufficientFundsError as e:
    print(e)  # Cannot withdraw $150. Balance: $100
Try Raising Exceptions
JavaScript
def set_age(age):
    if age < 0 or age > 150:
        raise ValueError(f"Invalid age: {age}")
    return age

# Valid age
try:
    print("Age set to:", set_age(25))
except ValueError as e:
    print(e)

# Invalid age
try:
    print("Age set to:", set_age(-5))
except ValueError as e:
    print("Error:", e)
Notes
  • Common built-in exceptions: ValueError, TypeError, KeyError, IndexError, FileNotFoundError, ZeroDivisionError, AttributeError, ImportError.