Python Custom Exceptions

In Python, exceptions are used to handle errors during the execution of a program. While Python provides many built-in exceptions, sometimes you may need to define your own exceptions to represent specific error conditions in your application. This is where custom exceptions come in. Custom exceptions allow you to handle errors more effectively and improve code clarity.

What are Custom Exceptions?

Custom exceptions are user-defined exceptions that you can create by subclassing the built-in Exception class. Custom exceptions are useful when you want to raise an error that is specific to your application, making it easier to identify and manage.

Creating a Custom Exception

To create a custom exception in Python, you need to define a new class that inherits from the built-in Exception class. You can then add your own custom logic and messages to the exception.

Syntax:

class CustomException(Exception):
    def __init__(self, message):
        self.message = message
        super().__init__(self.message)

Try It Now

1. Example: Creating a Simple Custom Exception

Let’s create a custom exception that is raised when a user provides invalid input in a program.

Example:

class InvalidInputError(Exception):
    def __init__(self, message):
        self.message = message
        super().__init__(self.message)

def validate_input(value):
    if value < 0:
        raise InvalidInputError("Input cannot be negative.")
    return f"Input {value} is valid."

try:
    print(validate_input(-5))
except InvalidInputError as e:
    print(f"Error: {e}")

Try It Now

In this example, we define a custom exception InvalidInputError and raise it when the user provides a negative value as input. The exception is then caught in the except block and an appropriate error message is printed.

2. Adding Custom Attributes to Custom Exceptions

You can add custom attributes to your exception class to provide additional information about the error, such as an error code or a timestamp.

Example:

class AuthenticationError(Exception):
    def __init__(self, message, error_code):
        self.message = message
        self.error_code = error_code
        super().__init__(self.message)

def authenticate_user(username, password):
    if username != "admin" or password != "password123":
        raise AuthenticationError("Invalid username or password.", 401)
    return "Authentication successful."

try:
    print(authenticate_user("user", "pass"))
except AuthenticationError as e:
    print(f"Error: {e.message} (Error code: {e.error_code})")

Try It Now

Here, we define an AuthenticationError exception with an error_code attribute. This gives us more flexibility to pass additional information when raising the exception, such as an HTTP error code.

3. Customizing Exception Messages

You can customize the error message when raising a custom exception to make the output more descriptive and easier to understand.

Example:

class InsufficientFundsError(Exception):
    def __init__(self, message, balance, amount):
        self.message = message
        self.balance = balance
        self.amount = amount
        super().__init__(self.message)

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError("Insufficient funds for this transaction.", balance, amount)
    return balance - amount

try:
    print(withdraw(100, 150))
except InsufficientFundsError as e:
    print(f"Error: {e.message}. Balance: {e.balance}, Requested amount: {e.amount}")

Try It Now

In this example, we define a custom exception InsufficientFundsError that includes additional information such as the current balance and the requested withdrawal amount.

4. Handling Multiple Custom Exceptions

You can define multiple custom exceptions and handle them individually using multiple except blocks. This allows you to implement specific logic for different error scenarios.

Example:

class FileNotFoundError(Exception):
    pass

class PermissionDeniedError(Exception):
    pass

def read_file(filename):
    if filename == "missing.txt":
        raise FileNotFoundError("File not found.")
    elif filename == "protected.txt":
        raise PermissionDeniedError("Permission denied.")
    return "File content"

try:
    print(read_file("missing.txt"))
except FileNotFoundError as e:
    print(f"Error: {e}")
except PermissionDeniedError as e:
    print(f"Error: {e}")

Try It Now

Here, we define two custom exceptions, FileNotFoundError and PermissionDeniedError, and handle them separately using two different except blocks. This allows us to respond to different types of errors in a more specific way.

Conclusion

Custom exceptions in Python are a powerful tool for handling specific error conditions in your application. By defining your own exceptions, you can make your code more readable, modular, and easier to debug. Whether you're validating user input, managing file access, or handling complex business logic, custom exceptions help you improve error handling and create a more robust application.