24.3 The tryexceptelse Statement

24.3 The try/except/else Statement

The try is another compound statement; its most complete form is sketched below. It starts with a try header line followed by a block of (usually) indented statements, then one or more except clauses that identify exceptions to be caught, and an optional else clause at the end. The words try, except, and else are associated by indenting them the same they line up vertically. For reference, here's the general format:

try:     <statements>         # Run this action first. except <name1>:     <statements>         # Run if name1 is raised during try block. except <name2>, <data>:     <statements>         # Run if name2 is raised, and get extra data. except (name3, name4):     <statements>         # Run if any of these exceptions occur. except:     <statements>         # Run for all (other) exceptions raised. else:     <statements>         # Run if no exception was raised by try block.

In this statement, the block under the try header represents that main action of the statement the code you're trying to run. The except clauses define handlers for exceptions raised during the try block, and the else clause (if coded) provides a handler to be run if no exception occurs. The <data> entry here has to do with a feature of raise statements we will discuss later in this chapter.

Here's how try statements work. When a try statement is started, Python marks the current program context, so it can come back if an exception occurs. The statements nested under the try header are run first. What happens next depends on whether exceptions are raised while the try block's statements are running:

  • If an exception occurs while the try block's statements are running, Python jumps back to the try and runs the statements under the first except clause that matches the raised exception. Control continues past the entire try statement after the except block runs (unless the except block raises another exception).

  • If an exception happens in the try block and no except clause matches, the exception is propagated up to a try that was entered earlier in the program, or to the top level of the process (which makes Python kill the program and print a default error message).

  • If no exception occurs while the statements under the try header run, Python runs the statements under the else line (if present), and control then resumes past the entire try statement.

In other words, except clauses catch exceptions that may happen while the try block is running, and the else clause is run only if no exceptions happen while the try block runs.

The except clauses are focused exception handlers they catch exceptions that occur only within the statements in the associated try block. However, since the try block's statements can call functions coded elsewhere in a program, the source of an exception may be outside the try statement itself. More on this when we explore try nesting in Chapter 26.

24.3.1 Try Statement Clauses

When you write try statements, a variety of clauses can appear after the try statement block; Table 24-1 summarizes all the possible forms, and you must use at least one. We've already met some of these except clauses catch exceptions, finally runs on the way out, and so on. Syntactically, there may be any number of except clauses, but there should be only one else. Moreover, the finally clause must appear alone (without else or except); it's really a different statement.

Table 24-1. try statement clause forms

Clause form

Interpretation

except:

Catch all (other) exception types.

except name:

Catch a specific exception only.

except name, value:

Catch exception and its extra data.

except (name1, name2):

Catch any of the listed exceptions.

except (name1, name2), value

Catch any, and get the extra data.

else:

Run block if no exceptions raised.

finally:

Always perform block.

We'll explore the entries with the extra value part when we meet the raise statement. The first and fourth entries in Table 24-1 are new here:

  • except clauses that list no exception name catch all exceptions not previously listed in the try statement (except:).

  • except clauses that list a set of exceptions in parenthesis catch any of the listed exceptions (except (e1,e2,e3)).

Since Python looks for a match within a given try by inspecting except clauses from top to bottom, the parenthesized version is like listing each exception in its own except clause, but the statement body needs to be coded only once. Here's an example of multiple except clauses at work, which demonstrates just how specific your handlers can be:

try:     action(  ) except NameError:     ...  except IndexError     ... except KeyError:     ... except (AttributeError, TypeError, SyntaxError):     ... else:     ...

In this example, when an exception is raised while the call to the action function is running, Python returns to the try and searches for the first except that names the exception raised. It inspects except clauses from top to bottom and left to right, and runs the statements under the first one that matches. If none match, the exception is propagated past this try.

Note that the else runs only when no exception occurred in action, not for other exceptions raised. If you really want a general catch-all clause, an empty except does the trick:

try:     action(  ) except NameError:     ...                   # Handle NameError. except IndexError     ...                   # Handle IndexError. except:     ...                   # Handle all other exceptions. else:     ...                   # Handle the no-exception case.

The empty except clause is a sort of wildcard feature because it catches everything, it allows your handlers to be as general or specific as you like. In some scenarios, this form may be more convenient than listing all possible exceptions in a try. For example, the following catches everything without listing anything:

try:     action(  ) except:     ...          # Catch all possible exceptions.

Empty excepts also raise some design issues. Although convenient, they may also catch unexpected system exceptions unrelated to your code, and may inadvertently intercept exceptions meant for another handler. For example, even system exit calls in Python trigger exceptions, and you usually want these to pass. We'll revisit this as a gotcha at the end of Part VII. For now, we'll just say: use with care.

24.3.2 The try/else Clause

At first glance, the purpose of the else clause is not always obvious. Without it, though, there is no way to tell, without setting and checking Boolean flags, whether we wound up past a try statement because no exception happened, or because an exception occurred and was handled:

try:     ...run code... except IndexError:     ...handle exception... # Did we get here because the try failed or not?

Much like else clauses in loops, the else provides syntax here that makes this case obvious and unambiguous:

try:     ...run code... except IndexError:     ...handle exception... else:     ...no exception occurred...

You can almost emulate an else clause by moving its code to the end of the try block:

try:     ...run code...     ...no exception occurred... except IndexError:     ...handle exception...

This can lead to incorrect exception classifications, though. If the "no exception occurred" action triggers IndexError, it will register as a failure of the try block, and hence erroneously trigger the exception handler below the try (subtle, but true!). By using an explicit else clause instead, you make the logic more obvious, and guarantee that except handlers only run for real failures in the code you're wrapping in try, not for failures in the else case's action.

24.3.3 Example: Default Behavior

Since control flow through a program is easier to capture in Python than in English, let's run some examples that further illustrate exception basics. Exceptions not caught by try statements reach the top level of a Python process and run Python's default exception-handling logic. Python terminates the running program and prints a standard error message. For example, running the following module, bad.py, generates a divide-by-zero exception:

def gobad(x, y):     return x / y def gosouth(x):     print gobad(x, 0) gosouth(1)

Since the program ignores the exception it triggers, Python kills the program and prints a message this time, with useful file and line number information:[3]

[3] We should point out that the text of error messages and stack traces tends to vary slightly over time. Don't be alarmed if your error messages don't exactly match ours.

% python bad.py Traceback (most recent call last):   File "bad.py", line 7, in ?     gosouth(1)   File "bad.py", line 5, in gosouth     print gobad(x, 0)   File "bad.py", line 2, in gobad     return x / y ZeroDivisionError: integer division or modulo by zero

When an uncaught exception occurs, Python ends the program, and prints a stack trace and the name and any extra data of the exception that was raised. The stack trace lists all lines active when the exception occurred, from oldest to newest. For example, you can see that the bad divide happens at the last entry in the trace line 2 of file bad.py, a return statement.

Because Python detects and reports all errors at runtime by raising exceptions, exceptions are intimately bound up with the idea of error handling in general. For instance, if you've worked through the examples, you've undoubtedly seen an exception or two along the way even typos usually generate a SyntaxError or other exception when a file is imported or executed (that's when the compiler is run). By default, you get a useful error display like the one above, which helps track down the problem.

Often this standard error message is all you need to resolve a problem in your code. For more heavy duty debugging jobs, you can catch exceptions with try statements, or use debugging tools we'll introduce in Chapter 26.

24.3.4 Example: Catching Built-in Exceptions

Python's default exception handling is often exactly what you want especially for code in top-level script files, an error generally should terminate your program immediately. For many programs, there is no need to be more specific about errors in your code.

Sometimes, though, you'll want to catch errors and recover from them instead. If you don't want your program terminated when an exception is raised by Python, simply catch it by wrapping program logic in a try. For example, the following code catches and recovers from the TypeError Python raises immediately when we try to concatenate a list and a string (the + operator wants the same sequence type on both sides):

def kaboom(x, y):     print x + y                    # Trigger TypeError. try:     kaboom([0,1,2], "spam") except TypeError:                  # Catch and recover here.     print 'Hello world!' print 'resuming here'              # Continue here if exception or not.

When the exception occurs in function kaboom, control jumps to the try statement's except clause, which prints a message. Since an exception is "dead" after it's been caught like this, the program continues past the whole try, rather than being terminated by Python. In effect, your code processes and clears the error.

Notice that once you've caught the error, control resumes at the place where you caught it, after the try; there is no direct way to go back to the place where the exception occurred (function kaboom). In a sense, this makes exceptions more like simple jumps than function calls there is no way to return to the code that triggered the error.



Learning Python
Learning Python: Powerful Object-Oriented Programming
ISBN: 0596158068
EAN: 2147483647
Year: 2003
Pages: 253
Authors: Mark Lutz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net