Concepts / Exception Class and Inheritance

Exception Class and Inheritance

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.

  • Programming
Interactive lab

Try it: try / except / else / finally

How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.

How it works

  1. Statements in try run until one raises an exception; the rest of try is skipped.
  2. Python checks the except clauses in order and runs the first one whose type matches.
  3. If nothing was raised, the else block runs instead.
  4. finally runs in every case.

Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.

Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.

Educational simulation

Loading the simulation…

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.

In the real world

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.

Key point

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.

ExceptionInventoryErrorinherited by
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__.

InventoryErrorone exception objectrequestedrequested quantityavailableavailable quantityhas attributehas attribute
What data is stored inside a custom exception object when it is initialized with domain-specific attributes?
python
Best practice

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.

try blockerror conditionraise InventoryErrorexcept InventoryErrorhandled responseexecutecondition occurscontrol jumpsprocess error
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.

InventoryErrorrequested, availableraiseerrorcaught objecterror.requestederror.availablecreate and passas erroraccessaccess
How does the caught exception object move into the except block, and where are its custom attributes accessed?
Worked example

Tracing an Inventory Failure

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

  1. 1
    Check the condition

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

  2. 2
    Raise the exception

    InventoryError is created with requested and available values, then raised.

  3. 3
    Skip the remaining try statements

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

  4. 4
    Bind the exception

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

  5. 5
    Read the attributes

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

Result

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

Choosing Exception Handlers

Best practice

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

  • Common mistake

    Defining the custom class without inheriting from Exception

    What it looks like
    class InventoryError:
        pass
    Why it fails

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

    Do this instead
    Declare it as class InventoryError(Exception).
  • Common mistake

    Failing to call Exception.__init__

    What it looks like
    def __init__(self, requested, available):
        self.requested = requested
        self.available = available
    Why it fails

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

    Do this instead

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

  • Common mistake

    Raising without the required arguments

    What it looks like

    raise InventoryError

    Why it fails

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

    Do this instead

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

  • Common mistake

    Ignoring the exception object in the except clause

    What it looks like
    except InventoryError:
        print("The request failed")
    Why it fails

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

    Do this instead

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

  • Common mistake

    Putting a general handler before a specific handler

    What it looks like
    except Exception:
        print("General error")
    except InventoryError as error:
        print(error.available)
    Why it fails

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

    Do this instead

    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. 1
    A custom exception inherits from Exception.
  2. 2
    Its __init__ method can store domain-specific error information as instance attributes.
  3. 3
    The custom __init__ method should call Exception.__init__.
  4. 4
    Raise the custom exception with all required arguments when its specific error condition occurs.
  5. 5
    Catch it with a matching except clause and the as keyword when the handler needs to access its attributes.
  6. 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.

Related concepts

More concepts starting with E