w3resource

Raising Exceptions in Python: How and When to Use Them

Introduction to Python Raising Eception

Raising exceptions in Python allows you to signal that something has gone wrong in your code, especially in cases where Python's built-in exceptions don't suffice or when you want to enforce specific rules or conditions. This tutorial will focus on practical examples to help you understand how and when to raise exceptions.

Example 1: Raising a Built-in Exception

In this example, the 'divide_numbers' function checks if the denominator is zero before performing division. If it is, a 'ZeroDivisionError' is raised, preventing the division and signaling the error. The exception is then caught and handled, demonstrating how to use 'raise' for input validation.

Code:

def divide_numbers(a, b):
    # Check if the denominator is zero
    if b == 0:
        # Raise a ZeroDivisionError if the denominator is zero
        raise ZeroDivisionError("Cannot divide by zero.")
    return a / b

try:
    # Attempt to divide 10 by 0, which will raise an exception
    result = divide_numbers(100, 0)
except ZeroDivisionError as e:
    # Handle the exception and print the error message
    print(f"Error: {e}")

Output:

Error: Cannot divide by zero.

Explanation:

  • raise: The 'raise' statement is used to trigger an exception manually.
  • ZeroDivisionError: A built-in exception that indicates a division by zero attempt.

Example 2: Raising a 'ValueError' for Invalid Input

This example raises a ValueError if the function calculate_square_root receives a negative input, which is not valid for calculating a square root. This ensures that the function only processes valid inputs and provides clear error messaging when it doesn't.

Code:

def calculate_square_root(x):
    # Check if the input is negative
    if x < 0:
        # Raise a ValueError for a negative input
        raise ValueError("Cannot calculate square root of a negative number.")
    return x ** 0.5

try:
    # Attempt to calculate the square root of -1, which will raise an exception
    result = calculate_square_root(-1)
except ValueError as e:
    # Handle the exception and print the error message
    print(f"Error: {e}")

Output:

Error: Cannot calculate square root of a negative number.

Explanation:

  • ValueError: A built-in exception that indicates an operation received an argument with the right type but an inappropriate value.

Example 3: Raising a Custom Exception

In this example, we define a custom exception 'InsufficientFundsError' to handle situations where a withdrawal amount exceeds the balance. This custom exception is raised within the 'withdraw_money' function, providing more specific error handling tailored to the application's needs.

Code:

class InsufficientFundsError(Exception):
    """Custom exception for insufficient funds."""
    pass

def withdraw_money(balance, amount):
    # Check if the withdrawal amount exceeds the balance
    if amount > balance:
        # Raise a custom InsufficientFundsError if funds are insufficient
        raise InsufficientFundsError("Insufficient funds for withdrawal.")
    return balance - amount

try:
    # Attempt to withdraw more money than the balance, raising an exception
    remaining_balance = withdraw_money(1000, 1500)
except InsufficientFundsError as e:
    # Handle the custom exception and print the error message
    print(f"Error: {e}")

Output:

Error: Insufficient funds for withdrawal.

Explanation:

  • Custom Exception: You can define your own exceptions by subclassing the built-in Exception class.
  • InsufficientFundsError: A custom exception created to handle cases where a withdrawal exceeds the available balance.

Example 4: Raising Exceptions in a Loop

This example demonstrates how to raise an exception within a loop. The function 'process_numbers' iterates over a list of numbers and raises a 'ValueError' if it encounters a negative number, halting the process and providing immediate feedback.

Code:

def process_numbers(numbers):
    for num in numbers:
        # Check if the number is negative
        if num < 0:
            # Raise a ValueError if a negative number is found
            raise ValueError(f"Negative number found: {num}")
        print(f"Processing number: {num}")

try:
    # Attempt to process a list of numbers that includes a negative number
    process_numbers([10, 20, -30, 40])
except ValueError as e:
    # Handle the exception and print the error message
    print(f"Error: {e}")

Output:

Processing number: 10
Processing number: 20
Error: Negative number found: -30

Explanation:

  • Raising exceptions within loops allows you to stop execution immediately if a condition is met.
  • This example raises a ‘ValueError’ when a negative number is encountered in the loop.

Example 5: Raising Exceptions in Functions

In this example, the 'validate_age' function ensures that the input age is within a realistic range (0 to 150). If the age is outside this range, a 'ValueError' is raised, preventing invalid data from being processed further.

Code:

def validate_age(age):
    # Check if the age is less than 0 or greater than 150
    if age < 0 or age > 150:
        # Raise a ValueError for an unrealistic age
        raise ValueError(f"Invalid age: {age}. Age must be between 0 and 150.")
    return age

try:
    # Attempt to validate an unrealistic age, raising an exception
    age = validate_age(200)
except ValueError as e:
    # Handle the exception and print the error message
    print(f"Error: {e}")

Output:

Error: Invalid age: 200. Age must be between 0 and 150.

Explanation:

  • Raising exceptions in functions allows you to enforce rules and constraints on input values.
  • This example raises a 'ValueError' for unrealistic age values.

Example 6: Using 'raise' to Re-Raise Exceptions

This example demonstrates how to re-raise an exception after handling it partially. The 'open_file' function attempts to open a file and raises a 'FileNotFoundError' if the file does not exist. The exception is caught, logged, and then re-raised to be handled elsewhere, showing how to propagate exceptions up the call stack.

Code:

def open_file(filename):
    try:
        # Attempt to open a file that may not exist
        file = open(filename, "r")
        return file.read()
    except FileNotFoundError:
        # Handle the exception and log the error
        print(f"File not found: {filename}")
        # Re-raise the exception for further handling
        raise

try:
    # Attempt to open a non-existent file, raising an exception
    content = open_file("test.txt")
except FileNotFoundError as e:
    # Handle the re-raised exception and print a final message
    print(f"Final handler: {e}")

Output:

File not found: test.txt
Final handler: [Errno 2] No such file or directory: 'test.txt'

Explanation:

  • raise: Without arguments, ‘raise’ re-raises the last exception, allowing it to be caught and handled further up the call stack.
  • This is useful when you want to handle an exception partially and let other parts of the program handle it more fully.

Example 7: Raising Exceptions in Object-Oriented Programming

This example now correctly includes the 'InsufficientFundsError' custom exception, which is raised when an attempt is made to withdraw more money than is available in the account. The deposit method still raises a 'ValueError' for invalid deposit amounts, and the code demonstrates how to handle these exceptions appropriately.

Code:

# Define the custom exception InsufficientFundsError
class InsufficientFundsError(Exception):
    """Custom exception for insufficient funds."""
    pass

class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            # Raise a ValueError if the deposit amount is not positive
            raise ValueError("Deposit amount must be positive.")
        self.balance += amount

    def withdraw(self, amount):
        if amount > self.balance:
            # Raise a custom InsufficientFundsError if the withdrawal amount exceeds the balance
            raise InsufficientFundsError("Insufficient funds for withdrawal.")
        self.balance -= amount

# Create a BankAccount instance with a balance of 100
account = BankAccount(100)

try:
    # Attempt to deposit a negative amount, raising an exception
    account.deposit(-50)
except ValueError as e:
    # Handle the exception and print the error message
    print(f"Error: {e}")

Output:

Error: Deposit amount must be positive.

Explanation:

  • InsufficientFundsError: The custom exception is defined by subclassing the built-in 'Exception' class. This exception is specific to the context of insufficient funds in the 'BankAccount' class.


Become a Patron!

Follow us on Facebook and Twitter for latest update.