Exception handling is an integral part of robust software development. Custom exceptions provide a way to create meaningful and descriptive error messages that make debugging easier and code more readable. This tutorial will guide you through creating and using custom exceptions in Python. ### Creating Custom Exceptions Creating a custom exception in Python involves extending the base `Exception` class. Here's a simple example:
class CustomError(Exception):
pass
# Raising the custom exception
def example_function(x):
if x < 0:
raise CustomError("CustomError: Negative value not allowed")
try:
example_function(-5)
except CustomError as e:
print(e)CustomError: Negative value not allowed
- **`class CustomError(Exception)`**: Defines a new custom exception type by extending the base `Exception` class.
- **`raise CustomError("CustomError: Negative value not allowed")`**: Raises the custom exception with a custom error message.
- **`try...except`**: Catches and handles the custom exception.
### Adding Custom Attributes and Methods
Custom exceptions can have additional attributes and methods to provide more context about the error.
class ValueTooSmallError(Exception):
def __init__(self, message, value):
super().__init__(message)
self.value = value
def example_function(x):
if x < 0:
raise ValueTooSmallError(f"ValueTooSmallError: {x} is less than 0", x)
try:
example_function(-5)
except ValueTooSmallError as e:
print(f"{e}: The value is {e.value}")ValueTooSmallError: -5 is less than 0: The value is -5
- **`__init__`**: Initializes the custom exception with an additional attribute `value`. - **`ValueTooSmallError`**: Custom error message includes the invalid value. ### Nested Custom Exceptions You can create a hierarchy of custom exceptions to represent various error types within your application.
class MyBaseError(Exception):
"""Base class for exceptions in this module."""
pass
class NetworkError(MyBaseError):
"""Exception raised for network-related errors."""
def __init__(self, message="Network error occurred"):
self.message = message
super().__init__(self.message)
class DatabaseError(MyBaseError):
"""Exception raised for database-related errors."""
def __init__(self, message="Database error occurred"):
self.message = message
super().__init__(self.message)
# Raising different custom exceptions
def network_operation():
raise NetworkError()
def database_operation():
raise DatabaseError()
try:
network_operation()
except NetworkError as e:
print(e)
except DatabaseError as e:
print(e)Network error occurred
- **`MyBaseError`**: Base class for all exceptions in this module. - **`NetworkError` and `DatabaseError`**: Derived classes for specific error types. ### Using Custom Exceptions Effectively Custom exceptions can significantly improve code readability and debugging. Here's a more comprehensive example demonstrating how to use custom exceptions in a practical scenario.
class InsufficientFundsError(Exception):
def __init__(self, balance, amount):
super().__init__(f"Insufficient funds: Cannot withdraw {amount}, balance is {balance}")
self.balance = balance
self.amount = amount
class BankAccount:
def __init__(self, balance):
self.balance = balance
def withdraw(self, amount):
if amount > self.balance:
raise InsufficientFundsError(self.balance, amount)
self.balance -= amount
return self.balance
# Testing the custom exception in a banking scenario
account = BankAccount(100)
try:
account.withdraw(150)
except InsufficientFundsError as e:
print(e)Insufficient funds: Cannot withdraw 150, balance is 100
- **`InsufficientFundsError`**: Custom exception class for insufficient funds. - **`withdraw()`**: Method that raises an exception if there are insufficient funds. ### Conclusion Creating and using custom exceptions in Python allows for more descriptive and meaningful error handling, making your code easier to understand and debug. By extending the base `Exception` class, adding custom attributes, and organizing exceptions hierarchically, you can effectively manage errors in your applications.