15-112 Lecture 5 (Monday, July 7, 2014)

Exceptions

Let's consider the following code. I know it doesn't work as written. But, what I'd like to do is to consider how it breaks:


def addNumbers():
  sum = 0

  while (True): 
    number = raw_input("number> ")
 
    sum = sum+int(number)

  return sum

print addNumbers()
  

It adds numbers, so long as it is fed integer numbers -- but then it dies when it is fed anything else:


Traceback (most recent call last):
  File "./example.py", line 13, in 
    print addNumbers()
  File "./example.py", line 9, in addNumbers
    sum = sum+int(number)
ValueError: invalid literal for int() with base 10: 'q'
  

Now, let's considered the revised version below. Any idea what it is doing?


def addNumbers():
  sum = 0

  while (True): 
    number = raw_input("number> ")
 
    try:
      sum = sum+int(number)
    except:
      break

  return sum

print addNumbers()
  

What do you think the try and except do? In effect, try tries to execute the associated block of code as normal, but if there is an error, looks to one or more associated except blocks for how to handle the error. In this case, we handle the error by breaking out of the loop.

So, now our function adds numbers provided by the usr, and lets the user stop by entering a non-number. Pretty cool, huh?

The Exception, as an Object

Exceptions are actually special types of complex entities called, "Objects". They contain data and associated functions, known as "methods".

When the user enters a non-number, and we try to use it as a number, Python generates an exception object to describe this situation. In effect, this object is realized as part of the "except:" block. In the code above, we used a short-hand "except:" syntax that let us know about the existence of the exception, but didn't actually allow us to get our hands on the exception object, itself.

Notice that, in the "except block" below, we use "as" to associate this object with the variable "e", and then print it. This is obviously not helpful to our user -- but it is a good example for us.


def addNumbers():
  sum = 0

  while (True): 
    number = raw_input("number> ")
 
    try:
      sum = sum+int(number)
    except:
      break

  return sum

print addNumbers()
  

The output of the "print e" above, is shown below:


invalid literal for int() with base 10: 'a'
  

Multiple Exception Types

The code below illustates the syntax for handling multiple exceptions within one "except:" block. Note that we are able to look for more than one exception in an except clause by listing them within a tuple. We'll learn more about tuples soon, but, in short, they are unchangable, ordered, ,-comma separated lists defiend within parentheses, e.g. "(4, 5, 6)", or, in the case below, "(ZeroDivisionError,TypeError)".


#!/usr/bin/python

def divideNumbersAndPrint(fmtString, x,y):
  try:
    result = x / y
    newString = fmtString % (result)
  except (ZeroDivisionError,TypeError):
    print "Invalid format string or divide by zero"

divideNumbersAndPrint("Result is %d", 5, 3)
divideNumbersAndPrint("Result is %f", 5.0, 3.0)

divideNumbersAndPrint("Result is %f %f", 5.0, 1.0)

divideNumbersAndPrint("Result is %f", 5.0, 0.0)
  

The example below is somewhat similar, except that we use multiple except blocks, which enables us to handle each exception differently:


#!/usr/bin/python

def divideNumbersAndPrint(fmtString, x,y):
  try:
    result = x / y
    newString = fmtString % (result)
  except ZeroDivisionError:
    print "Undefined" # As we called the result fo dividing by zero in high school
  except TypeError: 
    print result # Without broken formatting

divideNumbersAndPrint("Result is %d", 5, 3)
divideNumbersAndPrint("Result is %f", 5.0, 3.0)

divideNumbersAndPrint("Result is %f %f", 5.0, 1.0)

divideNumbersAndPrint("Result is %f", 5.0, 0.0)