Custom Exceptions in Python

Loading

In Python, exceptions are a way of handling errors or unusual conditions that may arise during the execution of a program. While Python provides a variety of built-in exceptions such as ValueError, TypeError, and FileNotFoundError, there may be situations where you need to define your own exceptions to handle specific cases unique to your program.

Custom exceptions allow you to create specialized error types for your application, making your error handling more clear, structured, and meaningful. By subclassing the built-in Exception class or one of its subclasses, you can create exceptions tailored to your needs.


1. Creating Custom Exceptions

To create a custom exception in Python, you need to subclass the base Exception class (or any of its subclasses). This allows you to define your own exception type, and optionally, add additional attributes or methods for better error reporting.

Basic Syntax:

class CustomException(Exception):
pass

Here, CustomException is a new exception class that inherits from Python’s built-in Exception class. The pass statement indicates that this exception doesn’t have any additional functionality, though you can extend it further.

Example: Basic Custom Exception

class NegativeAgeError(Exception):
pass

def validate_age(age):
if age < 0:
raise NegativeAgeError("Age cannot be negative.")
return age

try:
validate_age(-1)
except NegativeAgeError as e:
print(e)

Output:

Age cannot be negative.

Explanation:

  • NegativeAgeError is a custom exception class.
  • The function validate_age raises a NegativeAgeError if the provided age is negative.
  • The exception is caught in the except block, and the error message is printed.

2. Adding Custom Attributes to Custom Exceptions

You can extend custom exceptions by adding custom attributes. For example, you might want to capture additional information like an error code, a timestamp, or other context-specific data when an exception is raised.

Syntax:

class CustomException(Exception):
def __init__(self, message, code):
super().__init__(message) # Initialize the base Exception class with the message
self.code = code # Additional custom attribute

Example: Custom Exception with Additional Attributes

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

def validate_age(age):
if age < 0:
raise InvalidAgeError("Age cannot be negative", age)
return age

try:
validate_age(-1)
except InvalidAgeError as e:
print(f"Error: {e}, Age provided: {e.age}")

Output:

Error: Age cannot be negative, Age provided: -1

Explanation:

  • InvalidAgeError is a custom exception that takes both a message and an age as arguments.
  • The age is stored as an attribute, which can be accessed when the exception is caught.

3. Adding Custom Methods to Custom Exceptions

In addition to custom attributes, you can define custom methods within your exception class. These methods can provide additional functionality or return a more detailed string representation of the exception.

Example: Custom Method in Exception

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

def get_amount(self):
return f"The invalid amount was: {self.amount}"

def validate_amount(amount):
if amount <= 0:
raise InvalidAmountError("Amount must be positive", amount)
return amount

try:
validate_amount(-5)
except InvalidAmountError as e:
print(e.get_amount()) # Accessing custom method

Output:

The invalid amount was: -5

Explanation:

  • The InvalidAmountError exception has a custom method get_amount() that returns a string representation of the invalid amount.
  • This method is called when the exception is caught to provide more detailed information about the error.

4. Using Custom Exceptions in Complex Programs

Custom exceptions are especially useful when building larger applications that require specific error-handling for different parts of the program. By creating custom exception classes, you can manage errors in a more organized and descriptive manner.

Example: Using Custom Exceptions in an Application

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

class InsufficientStockError(Exception):
def __init__(self, message, product_id, requested_amount):
super().__init__(message)
self.product_id = product_id
self.requested_amount = requested_amount

class Inventory:
def __init__(self):
self.products = {
1: {"name": "Laptop", "stock": 5},
2: {"name": "Phone", "stock": 10},
}

def check_product(self, product_id):
if product_id not in self.products:
raise ProductNotFoundError(f"Product with ID {product_id} not found", product_id)

def check_stock(self, product_id, amount):
if self.products[product_id]["stock"] < amount:
raise InsufficientStockError(f"Insufficient stock for product ID {product_id}", product_id, amount)

def process_order(self, product_id, amount):
try:
self.check_product(product_id)
self.check_stock(product_id, amount)
print(f"Order for {amount} {self.products[product_id]['name']} processed.")
except (ProductNotFoundError, InsufficientStockError) as e:
print(e)

inventory = Inventory()
inventory.process_order(3, 2) # This will trigger ProductNotFoundError
inventory.process_order(1, 10) # This will trigger InsufficientStockError

Output:

Product with ID 3 not found
Insufficient stock for product ID 1

Explanation:

  • ProductNotFoundError and InsufficientStockError are custom exceptions used for handling specific error conditions related to product and stock issues in an inventory system.
  • The process_order method tries to check if the product exists and if the stock is sufficient. If either of those checks fails, the respective custom exception is raised, and the error is caught and printed.

5. Best Practices for Custom Exceptions

When creating custom exceptions, it’s important to follow certain guidelines to ensure your exception handling remains effective and maintainable:

Best Practices:

  1. Name Your Custom Exceptions Clearly: Name your exceptions in a way that clearly describes the error or condition they represent (e.g., NegativeAgeError, InsufficientStockError).
  2. Keep Custom Exceptions Simple: Avoid adding too many attributes or methods. Keep your custom exceptions simple and focused on the specific error condition.
  3. Use Custom Exceptions for Specific Cases: Custom exceptions are best used for error conditions that are specific to your application or domain. Use Python’s built-in exceptions for general errors.
  4. Provide Helpful Error Messages: When raising exceptions, provide clear and informative error messages to help diagnose the problem.

Leave a Reply

Your email address will not be published. Required fields are marked *