Understand the contract
A custom exception is usually a small class derived from Exception. Its name makes a domain failure distinguishable from unrelated built-in failures. Callers can catch that class without parsing text messages, which may change as diagnostics improve.
A shared application base exception lets a boundary catch all failures from one subsystem while narrower handlers distinguish individual problems. Most applications should derive from Exception rather than BaseException, which also covers interpreter-control events.
Follow the runtime behavior
Calling the exception class creates an instance; raise sends it through normal exception propagation. Initialize Exception with a useful message when adding structured attributes, so str(error) remains meaningful.
Catch specific subclasses before their base class because handlers are considered in order. Keep exception classes simple and avoid unrelated multiple inheritance among built-in exception types.
Apply it and practise
Expose stable attributes such as a product code or requested quantity when recovery needs more than a human-readable message. Keep secret data out of messages that may reach logs or users.
Exercise: create BookingError and SeatUnavailable subclasses, storing the requested seat on SeatUnavailable. Raise it for one occupied seat, catch it specifically, and print a suggested alternative without inspecting its message text.
Syntax
class InventoryError(Exception):
passExamples
Name a domain failure
Use a domain failure to let a checkout caller offer a restock notification.
class SoldOutError(Exception):
pass
def reserve(stock: int) -> int:
if stock == 0:
raise SoldOutError("no units remain")
return stock - 1
try:
reserve(0)
except SoldOutError as error:
print(error)- The custom class inherits message behavior from Exception. reserve raises that precise type, and the handler catches it without catching unrelated programming errors.
- Use a domain failure to let a checkout caller offer a restock notification.
- This simplified function does not handle negative stock or concurrent reservations; the custom type does not supply those rules.
- Expected output: no units remain
Carry structured context
Use structured error attributes when a UI needs to explain how far a request exceeds a limit.
class LimitError(Exception):
def __init__(self, limit: int, requested: int):
self.limit = limit
self.requested = requested
super().__init__(f"requested {requested}; limit {limit}")
try:
raise LimitError(5, 8)
except LimitError as error:
print(error)
print(error.requested - error.limit)- The initializer stores machine-readable fields and passes a readable message to the base exception. The handler calculates the excess from fields rather than parsing text.
- Use structured error attributes when a UI needs to explain how far a request exceeds a limit.
- Attributes are ordinary mutable data; the class does not automatically validate them.
- Expected output: requested 8; limit 5 3
Catch a family of errors
Use one subsystem base class at a command boundary that reports all expected import failures consistently.
class ImportFailure(Exception):
pass
class MissingColumn(ImportFailure):
pass
try:
raise MissingColumn("email")
except ImportFailure as error:
print(type(error).__name__)
print(error)- MissingColumn inherits ImportFailure, so the base-class handler accepts it while the instance retains its more specific type.
- Use one subsystem base class at a command boundary that reports all expected import failures consistently.
- A broad base handler can hide distinctions needed for recovery; add earlier specific handlers when different actions are necessary.
- Expected output: MissingColumn email
Best practices
- Catch specific subclasses before their base class because handlers are considered in order. Keep exception classes simple and avoid unrelated multiple inheritance among built-in exception types.
- Expose stable attributes such as a product code or requested quantity when recovery needs more than a human-readable message. Keep secret data out of messages that may reach logs or users.
Runtime & versions
Python interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.
Python language reference