Concepts / Exception Class and Inheritance

Exception Class and Inheritance

Custom exceptions inherit from Exception and let you encode domain-specific error information as instance attributes.

  • Programming

Why Custom Exceptions Matter

A custom exception gives a program a named way to represent a domain-specific error. Instead of treating every failure as an undifferentiated error, you can define an exception class for a particular condition and store information about that condition on the exception object.

Suppose an inventory operation cannot fulfill a request because too few units are available. An InventoryError could carry the requested quantity and the available quantity. Code that catches the exception can then use those attributes when deciding how to respond.

The central pattern is: define a custom exception, initialize its domain-specific data, raise it when the condition occurs, and catch it with an except clause that names the exception object.

The Inheritance Relationship

A custom exception is defined by inheriting from Exception. The custom class gives the error a domain-specific name and can add instance attributes that describe the particular failure. The parent class is still important: the custom exception remains an Exception-derived error that can be used with raise and caught by an except clause.

inherited byExceptionInventoryError
How does a domain-specific exception relate to the Exception class?
python

Storing Error Details

A custom exception can encode domain-specific error information as instance attributes. Its __init__ method receives the information needed to describe one occurrence of the error, assigns that information to attributes, and calls Exception.__init__.

has attributehas attributeInventoryErrorone exception objectrequestedrequested quantityavailableavailable quantity
What data is stored inside a custom exception object when it is initialized with domain-specific attributes?
python

Always call Exception.__init__ in the custom exception's __init__ method. The custom attributes provide domain-specific information, while the parent initialization remains part of constructing the exception.

Following the Raise Path

The raise statement is the point where the program signals the specific error condition. When the condition is reached, control flow jumps immediately from raise to the first matching except block. The remaining statements in the try block are skipped.

executecondition occurscontrol jumpsprocess errortry blockerror conditionraise InventoryErrorexcept InventoryErrorhandled response
What happens to control flow when a specific error condition raises a custom exception and a try-except block catches it?
python
Output
5
3

In this example, the call to withdraw raises InventoryError before return runs. The print inside the try block is therefore skipped. The except clause catches the exception as error, and error provides access to the requested and available attributes.

Reading the Caught Object

The as keyword gives the caught exception object a name inside the except block. That name refers to the custom exception instance created when the exception was raised. Its custom attributes can then be accessed using normal attribute access.

create and passas erroraccessaccessInventoryErrorrequested, availableraiseerrorcaught objecterror.requestederror.available
How does the caught exception object move into the except block, and where are its custom attributes accessed?

Tracing an Inventory Failure

Determine which statements run when withdraw is called with stock equal to 3 and requested equal to 5.

Check the condition: The requested quantity is greater than the stock quantity, so the specific error condition occurs.

Raise the exception: InventoryError is created with requested and available values, then raised.

Skip the remaining try statements: Control flow leaves the try block immediately, so statements after the call in that block do not run.

Bind the exception: The matching except clause binds the caught exception object to error.

Read the attributes: The handler accesses error.requested and error.available to obtain the domain-specific values.

The handler receives the custom exception object and can access both stored attributes.

Choosing Exception Handlers

Order except clauses from most specific to most general. A handler for the custom exception should appear before a more general handler so that the specific condition can be handled as intended.

python

Mistakes Beginners Make

  • Defining the custom class without inheriting from Exception

    The class is not defined as a custom exception derived from Exception.

    Fix: Declare it as class InventoryError(Exception).

  • Failing to call Exception.__init__

    The custom initialization stores the domain attributes but omits the required parent initialization.

    Fix: Call Exception.__init__(self) inside the custom __init__ method.

  • Raising without the required arguments

    The custom exception's initialization requires the domain-specific arguments used to set its attributes.

    Fix: Raise it with all required arguments, such as raise InventoryError(requested, stock).

  • Ignoring the exception object in the except clause

    The handler does not bind the caught object, so it cannot access the custom attributes through a local exception name.

    Fix: Use the as keyword, such as except InventoryError as error, when the handler needs the stored attributes.

  • Putting a general handler before a specific handler

    Exception handlers should be ordered from most specific to most general.

    Fix: Place except InventoryError before except Exception.

Practice the Full Pattern

MEDIUM

Create a custom exception named PaymentError that inherits from Exception. Give it two instance attributes, attempted and limit. Write a function that raises PaymentError when attempted is greater than limit. Then write a try-except block that catches the exception as error and accesses both custom attributes.

Hints
  • Define __init__ with attempted and limit parameters.
  • Assign both parameters to instance attributes.
  • Call Exception.__init__(self) in __init__.
  • Use raise PaymentError(attempted, limit) when the condition occurs.
  • Use except PaymentError as error to access error.attempted and error.limit.

What do you think happens?

If a raise statement runs inside a try block, do statements later in that try block run before the matching except handler?

  • Yes, all remaining try statements run first
  • No, control jumps directly to the first matching except block
Reveal answer

Answer: No, control jumps directly to the first matching except block.

The source rule states that control flow jumps immediately from raise to the first matching except block, skipping the rest of the try block.

The Complete Pattern

  1. A custom exception inherits from Exception.
  2. Its __init__ method can store domain-specific error information as instance attributes.
  3. The custom __init__ method should call Exception.__init__.
  4. Raise the custom exception with all required arguments when its specific error condition occurs.
  5. Catch it with a matching except clause and the as keyword when the handler needs to access its attributes.
  6. Control flow leaves the try block immediately when raise runs, and except clauses should be ordered from most specific to most general.

Key Takeaways

  • Custom exceptions are Exception subclasses that give domain-specific errors a clear type.
  • Instance attributes let a custom exception carry information about the particular error occurrence.
  • A custom exception should initialize its parent with Exception.__init__.
  • raise transfers control immediately to the first matching except clause.
  • The as keyword binds the caught exception object so its custom attributes can be accessed.