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.
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
- Statements in try run until one raises an exception; the rest of try is skipped.
- Python checks the except clauses in order and runs the first one whose type matches.
- If nothing was raised, the else block runs instead.
- 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.
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.
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.
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__.
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.
5
3In 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.
Tracing an Inventory Failure
Determine which statements run when withdraw is called with stock equal to 3 and requested equal to 5.
- 1Check the condition
The requested quantity is greater than the stock quantity, so the specific error condition occurs.
- 2Raise the exception
InventoryErroris created with requested and available values, then raised. - 3Skip the remaining try statements
Control flow leaves the try block immediately, so statements after the call in that block do not run.
- 4Bind the exception
The matching except clause binds the caught exception object to error.
- 5Read 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.
Mistakes Beginners Make
Common mistake Defining the custom class without inheriting from Exception
What it looks likeclass InventoryError: passWhy it failsThe class is not defined as a custom exception derived from Exception.
Do this insteadDeclare it as class InventoryError(Exception).Common mistake Failing to call Exception.__init__
What it looks likedef __init__(self, requested, available): self.requested = requested self.available = availableWhy it failsThe custom initialization stores the domain attributes but omits the required parent initialization.
Do this insteadCall Exception.__init__(self) inside the custom __init__ method.
Common mistake Raising without the required arguments
What it looks likeraise InventoryError
Why it failsThe custom exception's initialization requires the domain-specific arguments used to set its attributes.
Do this insteadRaise it with all required arguments, such as raise InventoryError(requested, stock).
Common mistake Ignoring the exception object in the except clause
What it looks likeexcept InventoryError: print("The request failed")Why it failsThe handler does not bind the caught object, so it cannot access the custom attributes through a local exception name.
Do this insteadUse 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 likeexcept Exception: print("General error") except InventoryError as error: print(error.available)Why it failsException handlers should be ordered from most specific to most general.
Do this insteadPlace except InventoryError before except Exception.
Practice the Full Pattern
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
PaymentErroras 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?
Reveal 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
- 1A custom exception inherits from Exception.
- 2Its
__init__method can store domain-specific error information as instance attributes. - 3The custom
__init__method should call Exception.__init__. - 4Raise the custom exception with all required arguments when its specific error condition occurs.
- 5Catch it with a matching except clause and the as keyword when the handler needs to access its attributes.
- 6Control 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.