Defining your own exception class, typically by subclassing Exception with little or no extra code, lets calling code catch exactly the errors your application can produce, distinctly from unrelated built-in exceptions like ValueError or KeyError. A custom exception can also carry extra data beyond just a message, by defining its own __init__ that stores additional attributes, which lets except handlers programmatically inspect what specifically went wrong instead of just parsing an error string.
1Understanding Custom Exceptions
Defining your own exception class, typically by subclassing Exception with little or no extra code, lets calling code catch exactly the errors your application can produce, distinctly from unrelated built-in exceptions like ValueError or KeyError. A custom exception can also carry extra data beyond just a message, by defining its own __init__ that stores additional attributes, which lets except handlers programmatically inspect what specifically went wrong instead of just parsing an error string.
Build a small hierarchy of custom exceptions with one shared base class for your application/library, so callers can either catch a very specific error or catch the shared base class to handle any error from your code broadly.
class InsufficientFundsError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError("Not enough balance")
return balance - amount
try:
withdraw(50, 100)
except InsufficientFundsError as e:
print(e)2Practical Example
Here is a real-world application of Custom Exceptions showing how it is used in production Python code.
class ValidationError(Exception):
def __init__(self, field, message):
super().__init__(message)
self.field = field
try:
raise ValidationError("email", "Invalid email format")
except ValidationError as e:
print(f"{e.field}: {e}")3Best Practices
Follow these guidelines when working with Custom Exceptions:
1. Subclass Exception, not BaseException directly, for application-specific errors, so they don't accidentally get caught alongside system-exiting exceptions
2. Give custom exceptions clear, specific names ending in 'Error', matching Python's own naming convention
3. Store extra structured data as attributes on the exception instance, not just embedded in the message string, so handlers can inspect it programmatically
Tip: Build a small hierarchy of custom exceptions with one shared base class for your application/library, so callers can either catch a very specific error or catch the shared base class to handle any error from your code broadly.
class InsufficientFundsError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError("Not enough balance")
return balance - amount
try:
withdraw(50, 100)
except InsufficientFundsError as e:
print(e)