Learning Python - Mark Lutz [458]
Unified try Statement Syntax
When combined like this, the try statement must have either an except or a finally, and the order of its parts must be like this:
try -> except -> else -> finally
where the else and finally are optional, and there may be zero or more except, but there must be at least one except if an else appears. Really, the try statement consists of two parts: excepts with an optional else, and/or the finally.
In fact, it’s more accurate to describe the merged statement’s syntactic form this way (square brackets mean optional and star means zero-or-more here):
try: # Format 1
statements
except [type [as value]]: # [type [, value]] in Python 2
statements
[except [type [as value]]:
statements]*
[else:
statements]
[finally:
statements]
try: # Format 2
statements
finally:
statements
Because of these rules, the else can appear only if there is at least one except, and it’s always possible to mix except and finally, regardless of whether an else appears or not. It’s also possible to mix finally and else, but only if an except appears too (though the except can omit an exception name to catch everything and run a raise statement, described later, to reraise the current exception). If you violate any of these ordering rules, Python will raise a syntax error exception before your code runs.
Combining finally and except by Nesting
Prior to Python 2.5, it is actually possible to combine finally and except clauses in a try by syntactically nesting a try/except in the try block of a try/finally statement (we’ll explore this technique more fully in Chapter 35). In fact, the following has the same effect as the new merged form shown at the start of this section:
try: # Nested equivalent to merged form
try:
main-action
except Exception1:
handler1
except Exception2:
handler2
...
else:
no-error
finally:
cleanup
Again, the finally block is always run on the way out, regardless of what happened in the main action and regardless of any exception handlers run in the nested try (trace through the four cases listed previously to see how this works the same). Since an else always requires an except, this nested form even sports the same mixing constraints of the unified statement form outlined in the preceding section.
However, this nested equivalent is more obscure and requires more code than the new merged form (one four-character line, at least). Mixing finally into the same statement makes your code easier to write and read, so this is the generally preferred technique today.
Unified try Example
Here’s a demonstration of the merged try statement form at work. The following file, mergedexc.py, codes four common scenarios, with print statements that describe the meaning of each:
sep = '-' * 32 + '\n'
print(sep + 'EXCEPTION RAISED AND CAUGHT')
try:
x = 'spam'[99]
except IndexError:
print('except run')
finally:
print('finally run')
print('after run')
print(sep + 'NO EXCEPTION RAISED')
try:
x = 'spam'[3]
except IndexError:
print('except run')
finally:
print('finally run')
print('after run')
print(sep + 'NO EXCEPTION RAISED, WITH ELSE')
try:
x = 'spam'[3]
except IndexError:
print('except run')
else:
print('else run')
finally:
print('finally run')
print('after run')
print(sep + 'EXCEPTION RAISED BUT NOT CAUGHT')
try:
x = 1 / 0
except IndexError:
print('except run')
finally:
print('finally run')
print('after run')
When this code is run, the following output is produced in Python 3.0 (actually, its behavior and output are the same in 2.6, because the print calls each print a single item). Trace through the code to see how exception handling produces the output of each of the four tests here:
c:\misc> C:\Python30\python mergedexc.py
--------------------------------
EXCEPTION RAISED AND CAUGHT
except run
finally run
after run
--------------------------------
NO EXCEPTION RAISED
finally run
after run
--------------------------------
NO EXCEPTION