Section 10.3. Detecting and Handling Exceptions


10.3. Detecting and Handling Exceptions

Exceptions can be detected by incorporating them as part of a try statement. Any code suite of a TRy statement will be monitored for exceptions.

There are two main forms of the TRy statement: TRy-except and try-finally. These statements are mutually exclusive, meaning that you pick only one of them. A try statement can be accompanied by one or more except clauses, exactly one finally clause, or a hybrid try-except-finally combination.

try-except statements allow one to detect and handle exceptions. There is even an optional else clause for situations where code needs to run only when no exceptions are detected. Meanwhile, TRy-finally statements allow only for detection and processing of any obligatory cleanup (whether or not exceptions occur), but otherwise have no facility in dealing with exceptions. The combination, as you might imagine, does both.

10.3.1. try-except Statement

The TRy-except statement (and more complicated versions of this statement) allows you to define a section of code to monitor for exceptions and also provides the mechanism to execute handlers for exceptions.

The syntax for the most general try-except statement is given below. It consists of the keywords along with the try and except blocks (try_suite and except_suite) as well as optionally saving the reason of failure:

try:     try_suite       # watch for exceptions here except Exception[, reason]:     except_suite    # exception-handling code


Let us give one example, then explain how things work. We will use our IOError example from above. We can make our code more robust by adding a try-except "wrapper" around the code:

>>> try: ...     f = open('blah', 'r') ... except IOError, e: ...     print 'could not open file:', e ... could not open file: [Errno 2] No such file or directory


As you can see, our code now runs seemingly without errors. In actuality, the same IOError still occurred when we attempted to open the nonexistent file. The difference? We added code to both detect and handle the error. When the IOError exception was raised, all we told the interpreter to do was to output a diagnostic message. The program continues and does not "bomb out" as our earlier examplea minor illustration of the power of exception handling. So what is really happening codewise?

During runtime, the interpreter attempts to execute all the code within the try statement. If an exception does not occur when the code block has completed, execution resumes past the except statement. When the specified exception named on the except statement does occur, we save the reason, and control flow immediately continues in the handler (all remaining code in the TRy clause is skipped) where we display our error message along with the cause of the error.

In our example above, we are catching only IOError exceptions. Any other exception will not be caught with the handler we specified. If, for example, you want to catch an OSError, you have to add a handler for that particular exception. We will elaborate on the try-except syntax more as we progress further in this chapter.

Core Note: Skipping code, continuation, and upward propagation

The remaining code in the try suite from the point of the exception is never reached (hence never executed). Once an exception is raised, the race is on to decide on the continuing flow of control. The remaining code is skipped, and the search for a handler begins. If one is found, the program continues in the handler.

If the search is exhausted without finding an appropriate handler, the exception is then propagated to the caller's level for handling, meaning the stack frame immediately preceding the current one. If there is no handler at the next higher level, the exception is yet again propagated to its caller. If the top level is reached without an appropriate handler, the exception is considered unhandled, and the Python interpreter will display the traceback and exit.


10.3.2. Wrapping a Built-in Function

We will now present an interactive examplestarting with the bare necessity of detecting an error, then building continuously on what we have to further improve the robustness of our code. The premise is in detecting errors while trying to convert a numeric string to a proper (numeric object) representation of its value.

The float() built-in function has a primary purpose of converting any numeric type to a float. In Python 1.5, float() was given the added feature of being able to convert a number given in string representation to an actual float value, obsoleting the use of the atof() function of the string module. Readers with older versions of Python may still use string.atof(), replacing float(), in the examples we use here.

>>> float(12345) 12345.0 >>> float('12345') 12345.0 >>> float('123.45e67') 1.2345e+069


Unfortunately, float() is not very forgiving when it comes to bad input:

>>> float('foo') Traceback (innermost last):   File "<stdin>", line 1, in ?     float('foo') ValueError: invalid literal for float(): foo >>> >>> float(['this is', 1, 'list']) Traceback (innermost last):   File "<stdin>", line 1, in ?     float(['this is', 1, 'list']) TypeError: float() argument must be a string or a number


Notice in the errors above that float() does not take too kindly to strings that do not represent numbers or non-strings. Specifically, if the correct argument type was given (string type) but that type contained an invalid value, the exception raised would be ValueError because it was the value that was improper, not the type. In contrast, a list is a bad argument altogether, not even being of the correct type; hence, TypeErrorwas thrown.

Our exercise is to call float()"safely," or in a more "safe manner," meaning that we want to ignore error situations because they do not apply to our task of converting numeric string values to floating point numbers, yet are not severe enough errors that we feel the interpreter should abandon execution. To accomplish this, we will create a "wrapper" function, and, with the help of TRy-except, create the environment that we envisioned. We shall call it safe_float(). In our first iteration, we will scan and ignore only ValueErrors, because they are the more likely culprit. TypeErrors rarely happen since somehow a non-string must be given to float().

def safe_float(obj):    try:        return float(obj)    except ValueError:        pass


The first step we take is to just "stop the bleeding." In this case, we make the error go away by just "swallowing it." In other words, the error will be detected, but since we have nothing in the except suite (except the pass statement, which does nothing but serve as a syntactical placeholder for where code is supposed to go), no handling takes place. We just ignore the error.

One obvious problem with this solution is that we did not explicitly return anything to the function caller in the error situation. Even though None is returned (when a function does not return any value explicitly, i.e., completing execution without encountering a return object statement), we give little or no hint that anything wrong took place. The very least we should do is to explicitly return None so that our function returns a value in both cases and makes our code somewhat easier to understand:

def safe_float(obj):     try:          retval = float(obj)     except ValueError:         retval = None     return retval


Bear in mind that with our change above, nothing about our code changed except that we used one more local variable. In designing a well-written application programmer interface (API), you may have kept the return value more flexible. Perhaps you documented that if a proper argument was passed to safe_float(), then indeed, a floating point number would be returned, but in the case of an error, you chose to return a string indicating the problem with the input value. We modify our code one more time to reflect this change:

def safe_float(obj):      try:          retval = float(obj)      except ValueError:           retval = 'could not convert non-number to float'      return retval


The only thing we changed in the example was to return an error string as opposed to just None. We should take our function out for a test drive to see how well it works so far:

>>> safe_float('12.34') 12.34 >>> safe_float('bad input') 'could not convert non-number to float'


We made a good startnow we can detect invalid string input, but we are still vulnerable to invalid objects being passed in:

>>> safe_float({'a': 'Dict'}) Traceback (innermost last):   File "<stdin>", line 3, in ?     retval = float(obj) TypeError: float() argument must be a string or a number


We will address this final shortcoming momentarily, but before we further modify our example, we would like to highlight the flexibility of the try-except syntax, especially the except statement, which comes in a few more flavors.

10.3.3. try Statement with Multiple excepts

Earlier in this chapter, we introduced the following general syntax for except:

except Exception[, reason]:      suite_for_exception_Exception


The except statement in such formats specifically detects exceptions named Exception. You can chain multiple except statements together to handle different types of exceptions with the same TRy:

except Exception1[, reason1]:      suite_for_exception_Exception1 except Exception2[, reason2]:      suite_for_exception_Exception2                  :


This same try clause is attempted, and if there is no error, execution continues, passing all the except clauses. However, if an exception does occur, the interpreter will look through your list of handlers attempting to match the exception with one of your handlers (except clauses). If one is found, execution proceeds to that except suite.

Our safe_float()function has some brains now to detect specific exceptions. Even smarter code would handle each appropriately. To do that, we have to have separate except statements, one for each exception type. That is no problem as Python allows exceptstatements can be chained together. We will now create separate messages for each error type, providing even more detail to the user as to the cause of his or her problem:

def safe_float(obj):     try:          retval = float(obj)     except ValueError:         retval = 'could not convert non-number to float'     except TypeError:         retval = 'object type cannot be converted to float'     return retval


Running the code above with erroneous input, we get the following:

>>> safe_float('xyz') 'could not convert non-number to float' >>> safe_float(()) 'argument must be a string' >>> safe_float(200L) 200.0 >>> safe_float(45.67000) 45.67


10.3.4. except Statement with Multiple Exceptions

We can also use the same except clause to handle multiple exceptions. except statements that process more than one exception require that the set of exceptions be contained in a tuple:

except (Exception1, Exception2)[, reason]:    suite_for_Exception1_and_Exception2


The above syntax example illustrates how two exceptions can be handled by the same code. In general, any number of exceptions can follow an except statement as long as they are all properly enclosed in a tuple:

except (Exc1[, Exc2[, ... ExcN]])[, reason]:    suite_for_exceptions_Exc1_to_ExcN


If for some reason, perhaps due to memory constraints or dictated as part of the design that all exceptions for our safe_float()function must be handled by the same code, we can now accommodate that requirement:

def safe_float(obj):     try:         retval = float(obj)     except (ValueError, TypeError):         retval = 'argument must be a number or numeric string'     return retval


Now there is only the single error string returned on erroneous input:

>>> safe_float('Spanish Inquisition') 'argument must be a number or numeric string' >>> safe_float([]) 'argument must be a number or numeric string' >>> safe_float('1.6') 1.6 >>> safe_float(1.6) 1.6 >>> safe_float(932) 932.0


10.3.5. Catching All Exceptions

Using the code we saw in the previous section, we are able to catch any number of specific exceptions and handle them. What about cases where we want to catch all exceptions? The short answer is yes, we can definitely do it. The code for doing it was significantly improved in 1.5 when exceptions became classes. Because of this, we now have an exception hierarchy to follow.

If we go all the way up the exception tree, we find Exception at the top, so our code will look like this:

try:     : except Exception, e:      # error occurred, log 'e', etc.


Less preferred is the bare except clause:

try:     : except:      # error occurred, etc.


This syntax is not as "Pythonic" as the other. Although this code catches the most exceptions, it does not promote good Python coding style. One of the chief reasons is that it does not take into account the potential root causes of problems that may generate exceptions. Rather than investigating and discovering what types of errors may occur and how they may be prevented from happening, we have a catch-all that may not do the right thing.

We are not naming any specific exceptions to catchit does not give us any information about the possible errors that could happen in our TRy block. Another thing is that by catching all errors, you may be silently dropping important errors that really should be sent to the caller to properly take care of them. Finally, we do not have the opportunity to save the reason for the exception. Yes, you can get it through sys.exc_ info(), but then you would have to import sys and execute that functionboth of which can be avoided, especially if all we wanted was the instance telling us why the exception occurred. It is a distinct possibility that the bare exception clause will be deprecated in a future release of Python. (See also Core Style note).

One aspect of catching all exceptions that you need to be aware of is that there are several exceptions that are not due to an error condition. These two exceptions are SystemExit and KeyboardInterrupt. SystemExit is for when the current Python application wants to quit, and KeyboardInterrupt is when a user presses CTRL-C (^C) to terminate Python. These will be caught by both code snippets above when we really want to pass them upward. A typical workaround code pattern will look like this:

try:     : except (KeyboardInterupt, SystemExit):     # user wants to quit     raise                 # reraise back to caller except Exception:     # handle real errors


A few things regarding exceptions did change in Python 2.5. Exceptions were moved to new-style classes, a new "mother of all exception" classes named BaseException was installed, and the exception hierarchy was switched around (very slightly) to get rid of that idiom of having to create two handlers. Both KeyboardInterrupt and SystemExit have been pulled out from being children of Exception to being its peers:

- BaseException   |- KeyboardInterrupt   |- SystemExit   |- Exception      |- (all other current built-in exceptions)


You can find the entire exception hierarchy (before and after these changes) in Table 10.2.

The end result is that now you do not have to write the extra handler for those two exceptions if you have a handler for just Exception. This code will suffice:

try:     : except Exception, e:      # handle real errors


If you really want to catch all errors, you can still do that too, but use BaseException instead:

try:     : except BaseException, e:      # handle all errors


And of course, there is the less preferred bare except.

Core Style: Do not handle and ignore all errors

The try-except statement has been included in Python to provide a powerful mechanism for programmers to track down potential errors and perhaps to provide logic within the code to handle situations where it may not otherwise be possible, for example, in C. The main idea is to minimize the number of errors and still maintain program correctness. As with all tools, they must be used properly.

One incorrect use of TRy-except is to serve as a giant bandage over large pieces of code. By that we mean putting large blocks, if not your entire source code, within a try and/or have a large generic except to "filter" any fatal errors by ignoring them:

# this is really bad code try:     large_block_of_code  # bandage of large piece of code except Exception:     # same as except:     pass              # blind eye ignoring all errors


Obviously, errors cannot be avoided, and the job of TRy-except is to provide a mechanism whereby an acceptable problem can be remedied or properly dealt with, and not be used as a filter. The construct above will hide many errors, but this type of usage promotes a poor engineering practice that we certainly cannot endorse.

Bottom line: Avoid using try-except around a large block of code with a pass just to hide errors. Instead, either handle specific exceptions and ignore them (pass), or handle all errors and take a specific action. Do not do both (handle all errors, ignore all errors).


10.3.6. "Exceptional Arguments"

No, the title of this section has nothing to do with having a major fight. Instead, we are referring to the fact that an exception may have an argument or reason passed along to the exception handler when they are raised. When an exception is raised, parameters are generally provided as an additional aid for the exception handler. Although reasons for exceptions are optional, the standard built-in exceptions do provide at least one argument, an error string indicating the cause of the exception.

Exception parameters can be ignored in the handler, but the Python provides syntax for saving this value. We have already seen it in the syntax above: to access any provided exception reason, you must reserve a variable to hold the argument. This argument is given on the except header line and follows the exception type you are handling. The different syntaxes for the except statement can be extended to the following:

# single exception except Exception[, reason]:      suite_for_Exception_with_Argument # multiple exceptions except (Exception1, Exception2, ..., ExceptionN)[, reason]:      suite_for_Exception1_to_ExceptionN_with_Argument


reason is a class instance containing diagnostic information from the code raising the exception. The exception arguments themselves go into a tuple that is stored as an attribute of the class instance, an instance of the exception class from which it was instantiated. In the first alternate syntax above, reason is an instance of the Exception class.

For most standard built-in exceptions, that is, exceptions derived from StandardError, the tuple consists of a single string indicating the cause of the error. The actual exception name serves as a satisfactory clue, but the error string enhances the meaning even more. Operating system or other environment type errors, i.e., IOError, will also include an operating system error number that precedes the error string in the tuple.

Whether a reason contains just a string or a combination of an error number and a string, calling str(reason) should present a human-readable cause of an error. However, do not lose sight that reason is really a class instanceyou are only getting the error information via that class's special method __str__(). We have a complete treatment of special methods as we explore object-oriented programming in Chapter 13.

The only caveat is that not all exceptions raised in third-party or otherwise external modules adhere to this standard protocol of error string or error number and error string. We recommend you follow such a standard when raising your own exceptions (see Core Style note).

Core Style: Follow exception argument protocol

When you raise built-in exceptions in your own code, try to follow the protocol established by the existing Python code as far as the error information that is part of the tuple passed as the exception argument. In other words, if you raise a ValueError, provide the same argument information as when the interpreter raises a ValueError exception, and so on. This helps keep the code consistent and will prevent other applications that use your module from breaking.


The example below is when an invalid object is passed to the float() built-in function, resulting in a TypeError exception:

>>> try: ...    float(['float() does not', 'like lists', 2]) ... except TypeError, diag:# capture diagnostic info ...    pass ... >>> type(diag) <class 'exceptions.TypeError'> >>> >>> print diag float() argument must be a string or a number


The first thing we did was cause an exception to be raised from within the try statement. Then we passed cleanly through by ignoring but saving the error information. Calling the type() built-in function, we were able to confirm that our exception was indeed an instance of the TypeError exception class. Finally, we displayed the error by calling print with our diagnostic exception argument.

To obtain more information regarding the exception, we can use the special __class__ instance attribute, which identifies which class an instance was instantiated from. Class objects also have attributes, such as a documentation string and a string name that further illuminate the error type:

>>> diag                     # exception instance object <exceptions.TypeError instance at 8121378> >>> diag.__class__           # exception class object <class exceptions.TypeError at 80f6d50> >>> diag.__class__.__doc__   # exception class documentation string 'Inappropriate argument type.' >>> diag.__class__.__name__  # exception class name 'TypeError'


As we will discover in Chapter 13Classes and OOP the special instance attribute __class__ exists for all class instances, and the __doc__ class attribute is available for all classes that define their documentation strings.

We will now update our safe_float() one more time to include the exception argument, which is passed from the interpreter from within float()when exceptions are generated. In our last modification to safe_float(), we merged both the handlers for the ValueError and TypeError exceptions into one because we had to satisfy some requirement. The problem, if any, with this solution is that no clue is given as to which exception was raised or what caused the error. The only thing returned is an error string that indicated some form of invalid argument. Now that we have the exception argument, this no longer has to be the case.

Because each exception will generate its own exception argument, if we chose to return this string rather than a generic one we made up, it would provide a better clue as to the source of the problem. In the following code snippet, we replace our single error string with the string representation of the exception argument.

def safe_float(object):   try:       retval = float(object)   except (ValueError, TypeError), diag:       retval = str(diag)   return retval


Upon running our new code, we obtain the following (different) messages when providing improper input to safe_float(), even if both exceptions are managed by the same handler:

>>> safe_float('xyz') 'invalid literal for float(): xyz' >>> safe_float({}) 'object can't be converted to float'


10.3.7. Using Our Wrapped Function in an Application

We will now feature safe_float() in a mini application that takes a credit card transaction data file (carddata.txt) and reads in all transactions, including explanatory strings. Here are the contents of our example carddata.txt file:

% cat carddata.txt # carddata.txt previous balance 25 debits 21.64 541.24 25 credits -25 -541.24 finance charge/late fees 7.30 5


Our program, cardrun.py, is given in Example 10.1.

Example 10.1. Credit Card Transactions (cardrun.py)

We use safe_float() to process a set of credit card transactions given in a file and read in as strings. A log file tracks the processing.

1  #!/usr/bin/env python 2 3  def safe_float(obj): 4    'safe version of float()' 5    try: 6        retval = float(obj) 7    except (ValueError, TypeError), diag: 8        retval = str(diag) 9    return retval 10 11 def main(): 12   'handles all the data processing' 13   log = open('cardlog.txt', 'w') 14   try: 15       ccfile = open('carddata.txt', 'r') 16   except IOError, e: 17       log.write('no txns this month\n') 18       log.close() 19       return 20 21   txns = ccfile.readlines() 22   ccfile.close() 23   total = 0.00 24   log.write('account log:\n') 25 26   for eachTxn in txns: 27       result = safe_float(eachTxn) 28       if isinstance(result, float): 29           total += result 30           log.write('data... processed\n') 31       else: 32           log.write('ignored: %s' % result) 33   print '$%.2f (new balance)' % (total) 34   log.close() 35 36   if __name__ == '__main__': 37    main()

Line-by-Line Explanation
Lines 39

This chunk of code contains the body of our safe_float() function.

Lines 1134

The core part of our application performs three major tasks: (1) read the credit card data file, (2) process the input, and (3) display the result. Lines 14-22 perform the extraction of data from the file. You will notice that there is a TRy-except statement surrounding the file open.

A log file of the processing is also kept. In our example, we are assuming the log file can be opened for write without any problems. You will find that our progress is kept by the log. If the credit card data file cannot be accessed, we will assume there are no transactions for the month (lines 16-19).

The data are then read into the txns (transactions) list where it is iterated over in lines 26-32. After every call to safe_float(), we check the result type using the isinstance() built-in function. In our example, we check to see if safe_float() returns a string or float. Any string indicates an error situation with a string that could not be converted to a number, while all other values are floats that can be added to the running subtotal. The final new balance is then displayed as the final line of the main() function.

Lines 3637

These lines represent the general "start only if not imported" functionality.Upon running our program, we get the following output:

$ cardrun.py $58.94 (new balance)


Taking a peek at the resulting log file (cardlog.txt), we see that it contains the following log entries after cardrun.py processed the transactions found in carddata.txt:

$ cat cardl og.txt account log: ignored: invalid literal for float(): # carddata.txt ignored: invalid literal for float(): previous balance data... processed ignored: invalid literal for float(): debits data... processed data... processed data... processed ignored: invalid literal for float(): credits data... processed data... processed ignored: invalid literal for float(): finance charge/ late fees data... processed data... processed


10.3.8. else Clause

We have seen the else statement with other Python constructs such as conditionals and loops. With respect to try-except statements, its functionality is not that much different from anything else you have seen: The else clause executes if no exceptions were detected in the preceding try suite.

All code within the try suite must have completed successfully (i.e., concluded with no exceptions raised) before any code in the else suite begins execution. Here is a short example in Python pseudocode:

import 3rd_party_module log = open('logfile.txt', 'w') try:     3rd_party_module.function() except:     log.write("*** caught exception in module\n") else:     log.write("*** no exceptions caught\n") log.close()


In the preceding example, we import an external module and test it for errors. A log file is used to determine whether there were defects in the third-party module code. Depending on whether an exception occurred during execution of the external function, we write differing messages to the log.

10.3.9. finally Clause

A finally clause is one where its suite or block of code is executed regardless of whether an exception occurred or whether it was caught (or not). You may use a finally clause with TRy by itself or with try-except (with or without an else clause). The standalone try-finally is covered in the next section, so we will just focus on the latter here.

Starting in Python 2.5, you can use the finally clause (again) with TRy-except or try-except-else. We say "again" because believe it or not, it is not a new feature. This was a feature available in Python back in the early days but was removed in Python 0.9.6 (April 1992). At the time, it helped simplify the bytecode generation process and was easier to explain, and van Rossum believed that a unified try-except (-else)-finally would not be very popular anyway. How things change well over a decade later!

Here is what the syntax would look like with try-except-else-finally:

try:     A except MyException:     B else:     C finally:     D


The equivalent in Python 0.9.6 through 2.4.x. is the longer:

try:     try:          A     except MyException:          B     else:          C finally:      D


Of course, in either case, you can have more than one except clause, however the syntax requires at least one except clause and both the else and finally clauses are optional. A,B,C, and D are suites (code blocks). The suites will execute in that order as necessary. (Note the only flows possible are A-C-D [normal] and A-B-D [exception].) The finally block will be executed whether exceptions occur in A,B, and/or C. Code written with the older idiom will continue to run, so there are no backward-compatibility problems.

10.3.10. try-finally Statement

An alternative is to use finally alone with try. The try-finally statement differs from its try-except brethren in that it is not used to handle exceptions. Instead it is used to maintain consistent behavior regardless of whether or not exceptions occur. We know that the finally suite executes regardless of an exception being triggered within the try suite.

try:     try_suite finally:     finally_suite  # executes regardless


When an exception does occur within the try suite, execution jumps immediately to the finally suite. When all the code in the finally suite completes, the exception is reraised for handling at the next higher layer. Thus it is common to see a try-finally nested as part of a TRy-except suite.

One place where we can add a TRy-finally statement is by improving our code in cardrun.py so that we catch any problems that may arise from reading the data from the carddata.txt file. In the current code in Example 10.1, we do not detect errors during the read phase (using readlines()):

try:    ccfile = open('carddata.txt') except IOError:    log.write('no txns this month\n') txns = ccfile.readlines() ccfile.close()


It is possible for readlines() to fail for any number of reasons, one of which is if carddata.txt was a file on the network (or a floppy) that became inaccessible. Regardless, we should improve this piece of code so that the entire input of data is enclosed in the try clause:

try:     ccfile = open('carddata.txt', 'r')     txns = ccfile.readlines()     ccfile.close() except IOError:     log.write('no txns this month\n')


All we did was to move the readlines() and close() method calls to the TRy suite. Although our code is more robust now, there is still room for improvement. Notice what happens if there was an error of some sort. If the open succeeds, but for some reason th readlines() call does not, the exception will continue with the except clause. No attempt is made to close the file. Wouldn't it be nice if we closed the file regardless of whether an error occurred or not? We can make it a reality using TRy-finally:

try:     try:          ccfile = open('carddata.txt', 'r')          txns = ccfile.readlines()     except IOError:         log.write('no txns this month\n') finally:         ccfile.close()


This code snippet will attempt to open the file and read in the data. If an error occurs during this step, it is logged, and then the file is properly closed. If no errors occur, the file is still closed. (The same functionality can be achieved using the unified try-except-finally statement above.) An alternative implementation involves switching the try-except and try-finally clauses:

try:     try:         ccfile = open('carddata.txt', 'r')         txns = ccfile.readlines()     finally:         ccfile.close() except IOError:      log.write('no txns this month\n')


The code works virtually the same with some differences. The most obvious one is that the closing of the file happens before the exception handler writes out the error to the log. This is because finally automatically reraises the exception.

One argument for doing it this way is that if an exception happens within the finally block, you are able to create another handler at the same outer level as the one we have, so in essence, be able to handle errors in both the original try block as well as the finally block. The only thing you lose when you do this is that if the finally block does raise an exception, you have lost context of the original exception unless you have saved it somewhere.

An argument against having the finally inside the except is that in many cases, the exception handler needs to perform some cleanup tasks as well, and if you release those resources with a finally block that comes before the exception handler, you have lost the ability to do so. In other words, the finally block is not as "final" as one would think.

One final note: If the code in the finally suite raises another exception, or is aborted due to a return, break, or continue statement, the original exception is lost and cannot be reraised.

10.3.11. try-except-else-finally: aka the Kitchen Sink

We can combine all the varying syntaxes that we have seen so far in this chapter to highlight all the different ways you can handle exceptions:

try:    try_suite except Exception1:    suite_for_Exception1 except (Exception2, Exception3, Exception4):    suite_for_Exceptions_2_3_and_4 except Exception5, Argument5:    suite_for_Exception5_plus_argument except (Exception6, Exception7), Argument67:    suite_for_Exceptions6_and_7_plus_argument except:    suite_for_all_other_exceptions else:    no_exceptions_detected_suite finally:    always_execute_suite


Recall from above that using a finally clause combined with TRy-except or try-except-else is "new" as of Python 2.5. The most important thing to take away from this section regarding the syntax is that you must have at least one except clause; both the else and finally clauses are optional.



Core Python Programming
Core Python Programming (2nd Edition)
ISBN: 0132269937
EAN: 2147483647
Year: 2004
Pages: 334
Authors: Wesley J Chun

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