Complete Guide: What Error to Raise in Python
Understanding what error to raise in Python is essential for writing robust, maintainable code. This comprehensive guide walks you through the decision-making process and teaches you when to use each type of exception with practical examples.
Understanding Python's Exception Hierarchy #
Before diving into what error to raise, it's important to understand Python's built-in exception hierarchy. All exceptions inherit from BaseException, with most custom exceptions inheriting from Exception.
# Exception hierarchy visualization
try:
# Your code here
pass
except Exception as e:
# Catches most exceptions
print(f"Caught exception: {type(e).__name__}")
Step 1: Identify the Problem Type #
The first step in determining what error to raise is identifying what type of problem occurred. Here's a systematic approach:
Data Type Issues #
When the problem is related to incorrect data types, use TypeError:
🐍 Try it yourself
Data Value Issues #
When the data type is correct but the value is inappropriate, use ValueError:
🐍 Try it yourself
Step 2: Consider Object State and Attributes #
Missing Attributes #
Use AttributeError when an object lacks required attributes or methods:
class Car:
def __init__(self, make, model):
self.make = make
self.model = model
def get_license_plate(self):
if not hasattr(self, 'license_plate'):
raise AttributeError("Car has no license plate assigned")
return self.license_plate
def assign_license_plate(self, plate):
self.license_plate = plate
# Example usage
my_car = Car("Toyota", "Camry")
try:
plate = my_car.get_license_plate()
except AttributeError as e:
print(f"Missing attribute: {e}")
# Assign license plate and try again
my_car.assign_license_plate("ABC-123")
print(f"License plate: {my_car.get_license_plate()}")
Invalid Object State #
Use RuntimeError for problems that occur during runtime due to invalid state:
🐍 Try it yourself
Step 3: Handle Collection and Key Operations #
Dictionary Key Errors #
Use KeyError when accessing non-existent dictionary keys:
🐍 Try it yourself
Index Errors for Sequences #
Use IndexError when accessing invalid sequence indices:
def get_list_item(items, index):
"""Safely get item from list with custom error message"""
if not isinstance(items, (list, tuple)):
raise TypeError("Expected list or tuple")
if not isinstance(index, int):
raise TypeError("Index must be an integer")
if index >= len(items) or index < -len(items):
raise IndexError(f"Index {index} out of range for list of length {len(items)}")
return items[index]
# Example usage
my_list = ["apple", "banana", "cherry"]
try:
item = get_list_item(my_list, 5)
except IndexError as e:
print(f"Index error: {e}")
Step 4: Create Custom Exceptions for Domain-Specific Problems #
When built-in exceptions don't clearly represent your specific problem domain, create custom exceptions:
🐍 Try it yourself
Step 5: Best Practices for Error Messages #
Write Clear, Actionable Error Messages #
Good error messages should tell the user:
- What went wrong
- What was expected
- How to fix it (when possible)
🐍 Try it yourself
Common Mistakes to Avoid #
Don't Use Generic Exceptions for Specific Problems #
# Bad: Too generic
def divide(a, b):
if b == 0:
raise Exception("Error occurred") # Too vague!
# Good: Specific and clear
def divide(a, b):
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Both arguments must be numbers")
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b
Don't Raise the Wrong Exception Type #
# Bad: Using ValueError for type issues
def process_list(items):
if not isinstance(items, list):
raise ValueError("Not a list") # Wrong exception type!
# Good: Using correct exception type
def process_list(items):
if not isinstance(items, list):
raise TypeError("Expected list, got {type(items).__name__}")
Decision Flow Chart #
Here's a simple decision process for what error to raise in Python:
- Wrong data type? →
TypeError - Right type, wrong value? →
ValueError - Missing attribute/method? →
AttributeError - Missing dictionary key? →
KeyError - Invalid sequence index? →
IndexError - Invalid runtime state? →
RuntimeError - Not implemented yet? →
NotImplementedError - Domain-specific problem? → Custom exception
Summary #
Knowing what error to raise in Python improves code maintainability and debugging efficiency. Remember these key principles:
- Choose specific exception types over generic ones
- Write clear, actionable error messages
- Create custom exceptions for domain-specific problems
- Consider the exception hierarchy when catching errors
- Always provide context about what went wrong and why
By following this guide, you'll write more robust Python applications that are easier to debug and maintain.
Next Steps #
- Practice implementing error handling in your projects
- Learn about exception chaining and context
- Explore logging best practices for errors
- Study testing exception handling