⚡ Creating Custom Exceptions
Custom exceptions help you create specific error types for your applications. They make error handling more precise and provide better context about what went wrong.
# Simple custom exception
class ValidationError(Exception):
pass
# Using custom exception
def validate_age(age):
if age < 0:
raise ValidationError("Age cannot be negative")
if age > 150:
raise ValidationError("Age seems unrealistic")
return age
try:
age = validate_age(-5)
except ValidationError as e:
print(f"Validation failed: {e}")
🎯 Basic Custom Exceptions
Custom exceptions are classes that inherit from the built-in Exception class.
Simple Custom Exception
# Define custom exceptions
class InvalidEmailError(Exception):
pass
class PasswordTooShortError(Exception):
pass
# Use custom exceptions
def validate_email(email):
if "@" not in email:
raise InvalidEmailError("Email must contain @ symbol")
return email
def validate_password(password):
if len(password) < 8:
raise PasswordTooShortError("Password must be at least 8 characters")
return password
# Test validation
try:
email = validate_email("invalid-email")
except InvalidEmailError as e:
print(f"Email error: {e}")
try:
password = validate_password("123")
except PasswordTooShortError as e:
print(f"Password error: {e}")
Custom Exception with Additional Data
# Exception with extra information
class OutOfStockError(Exception):
def __init__(self, product, available):
self.product = product
self.available = available
message = f"Product '{product}' out of stock. Available: {available}"
super().__init__(message)
# Use exception with data
def purchase_item(product, quantity):
stock = {"apples": 5, "bananas": 0, "oranges": 3}
available = stock.get(product, 0)
if available < quantity:
raise OutOfStockError(product, available)
return f"Purchased {quantity} {product}"
try:
result = purchase_item("bananas", 2)
except OutOfStockError as e:
print(f"Error: {e}")
print(f"Product: {e.product}")
print(f"Available: {e.available}")
Exception Hierarchy
# Base custom exception
class APIError(Exception):
pass
# Specific API exceptions
class AuthenticationError(APIError):
pass
class RateLimitError(APIError):
pass
class ServerError(APIError):
pass
# Function that raises different errors
def api_call(status_code):
if status_code == 401:
raise AuthenticationError("Invalid credentials")
elif status_code == 429:
raise RateLimitError("Too many requests")
elif status_code >= 500:
raise ServerError("Server error occurred")
return "API call successful"
# Handle different error types
try:
result = api_call(429)
except AuthenticationError:
print("Please check your credentials")
except RateLimitError:
print("Please wait before making more requests")
except APIError:
print("General API error occurred")
📋 Custom Exception Patterns
Pattern | Purpose | Example |
---|---|---|
Simple | Basic custom error | class CustomError(Exception): pass |
With message | Custom error message | super().__init__(message) |
With data | Store additional info | self.code = code |
Hierarchy | Related error types | class SubError(BaseError): |
🔧 Advanced Custom Exceptions
Exception with Multiple Attributes
class ValidationError(Exception):
def __init__(self, field, value, message):
self.field = field
self.value = value
self.message = message
super().__init__(f"{field}: {message} (got: {value})")
def validate_user_data(name, age, email):
if not name:
raise ValidationError("name", name, "Name cannot be empty")
if age < 0:
raise ValidationError("age", age, "Age cannot be negative")
if "@" not in email:
raise ValidationError("email", email, "Invalid email format")
try:
validate_user_data("", 25, "test@email.com")
except ValidationError as e:
print(f"Validation failed:")
print(f" Field: {e.field}")
print(f" Value: {e.value}")
print(f" Message: {e.message}")
Exception with Error Codes
class BusinessError(Exception):
def __init__(self, code, message):
self.code = code
self.message = message
super().__init__(f"Error {code}: {message}")
# Define specific error codes
INSUFFICIENT_FUNDS = 1001
ACCOUNT_LOCKED = 1002
INVALID_TRANSACTION = 1003
def process_payment(amount, balance, account_status):
if account_status == "locked":
raise BusinessError(ACCOUNT_LOCKED, "Account is locked")
if amount > balance:
raise BusinessError(INSUFFICIENT_FUNDS, "Not enough funds")
if amount <= 0:
raise BusinessError(INVALID_TRANSACTION, "Invalid transaction amount")
try:
process_payment(100, 50, "active")
except BusinessError as e:
print(f"Payment failed: {e}")
print(f"Error code: {e.code}")
if e.code == INSUFFICIENT_FUNDS:
print("Consider making a smaller payment")
elif e.code == ACCOUNT_LOCKED:
print("Contact support to unlock account")
Re-raising Exceptions
class DatabaseError(Exception):
pass
class ConnectionError(DatabaseError):
pass
def connect_to_database():
# Simulate connection error
raise ConnectionError("Cannot connect to database")
def get_user_data(user_id):
try:
connect_to_database()
return {"id": user_id, "name": "User"}
except ConnectionError:
print("Database connection failed, using cache")
raise # Re-raise the exception
try:
user = get_user_data(123)
except DatabaseError as e:
print(f"Database operation failed: {e}")
📊 Exception Organization Reference
Exception Naming Conventions
Type | Naming Pattern | Example |
---|---|---|
General errors | [Purpose]Error | ValidationError |
Specific conditions | [Condition]Error | OutOfStockError |
API/Network | [Service]Error | APIError |
Data processing | [Data]Error | ParseError |
Exception Hierarchy Best Practices
Level | Purpose | Example |
---|---|---|
Base | Common error type | class APIError(Exception) |
Category | Specific error group | class AuthError(APIError) |
Specific | Exact error condition | class InvalidTokenError(AuthError) |
🎯 Key Takeaways
🚀 What's Next?
Learn debugging techniques to find and fix issues in your code effectively.
Continue to: Debug Code
Was this helpful?
Track Your Learning Progress
Sign in to bookmark tutorials and keep track of your learning journey.
Your progress is saved automatically as you read.