Our last chapter in this part of the book has to do with exceptions—events that can modify the flow of control through a program. In Python, exceptions can be both intercepted and triggered by our programs. They are processed by two new statements we’ll study in this chapter:
try
Catches exceptions raised by Python or a program
raise
Triggers an exception manually
With a few exceptions (pun intended), we’ll find that exception handling is simple in Python, because it’s integrated into the language itself as another high-level tool.
In a nutshell, exceptions let us jump around arbitrarily large chunks of a program. Remember that pizza-making robot we talked about in the last chapter? Suppose we took the idea seriously and actually built such a machine (there are worse hobbies, after all). To make a pizza, our culinary automaton would need to execute a plan, which we implement as a Python program. It would take an order, prepare the dough, add toppings, bake the pie, and so on.
Now, suppose that something goes very wrong during the “bake the pie” step. Perhaps the oven is broken. Or perhaps our robot miscalculates its reach and spontaneously bursts into flames. Clearly, we want to be able to jump to code that handles such states quickly (especially if our robot is melting all over the kitchen floor!). Since we have no hope of finishing the pizza task in such unusual cases, we might as well abandon the entire plan.
That’s exactly what exceptions let you do; you can jump to an
exception handler in a single step, past all suspended function
calls. They’re a sort of “super-goto.”[50] An exception handler (try
statement)
leaves a marker and executes some code.
Somewhere further ahead in the program, an exception is raised that
makes Python jump back to the marker immediately, without resuming
any active functions that were called since the marker was left. Code
in the exception handler can respond to the raised exception as
appropriate (calling the fire department, for instance). Moreover,
because Python jumps to the handler statement immediately, there is
usually no need to check status codes after every call to a function
that could possibly fail.
In typical Python programs, exceptions may be used for a variety of things:
Python raises exceptions when it detects errors in programs at runtime; you can either catch and respond to the errors internally in your programs or ignore the exception. If ignored, Python’s default exception-handling behavior kicks in; it kills the program and prints an error message showing where the error occurred.
Exceptions can also signal a valid condition, without having to pass result flags around a program or test them explicitly. For instance, a search routine might raise an exception on success, rather than return an integer 1.
Sometimes a condition may happen so rarely that it’s hard to justify convoluting code to handle it. You can often eliminate special-case code by handling unusual cases in exception handlers instead.
And finally, because exceptions are a type of high-level goto, you can use them as the basis for implementing exotic control flows. For instance, although backtracking is not part of the language itself, it can be implemented in Python with exceptions and a bit of support logic to unwind assignments.[51]
We’ll see some of these typical uses in action later in this chapter. First, let’s get started with a closer look at Python’s exception-processing tools.
[50] In fact, if you’ve used C, you may be interested to know
that Python exceptions are roughly equivalent to C’s
setjmp
/longjmp
standard
function pair. The try
statement acts much like a
setjmp
, and raise
works like a
longjmp
. But in Python, exceptions are based on
objects and are a standard part of the execution model.
[51] Backtracking isn’t part of the Python language, so we won’t say more about it here. See a book on artificial intelligence or the Prolog or icon programming languages if you’re curious.