Lecture 12

Exception Handling

We've been experiencing errors in our programs for weeks, e.g.:

So far, we hadn't seen a way to handle these errors. The solution is exception handling. Exception handling sets up a special environment to try executing statements, then catch exceptions that may be raised. A basic form of this try-catch environment looks like this in python:

try:
    statements to execute
except ExceptionName:
    statements to execute when ExceptionName occurs

Both try and except are Python keywords. try sets up an exception handling environment. except is an exception clause (or except clause). Any exceptions raised may be caught and handled by an appropriate except clause.

The statements to execute when ExceptionName occurs will only execute if ExceptionName occurs. If that exception does not occur, then those statements will not execute.

For example:

value = '13'
try:
    value = int(value)
except ValueError:
    print('Invalid value; cannot convert into an int')

Why is the ExceptionName ValueError?

Similarly, when an exception is raised, normal flow of execution stops and Python starts looking for an exception handler to catch the exception. For example, in the code below, only the first print statement will execute:

try:
    print('a')
    int('13.8')
    print('this will never print')
except ValueError:
    print('this will print because ValueError is raised.')

Now, it's possible that different kinds of exceptions could be raised. An exception handling environment can deal with this in a couple of ways:

  1. Treat each error as different: vals = ['a', 'b', 'c', 'd']
    pos = input("Get the value at what position? ")
    try:
        pos = int(pos)
        value = vals[pos]
    except ValueError:
        print('Unable to convert "'+pos+'" into an integer.')
    except IndexError:
        print('Invalid position in list.')
    What does pos hold if an exception is raised?
  2. Treat all errors as the same: vals = ['a', 'b', 'c', 'd']
    pos = input("Get the value at what position? ")
    try:
        pos = int(pos)
        value = vals[pos]
    except ValueError, IndexError:
        print('Unable to perform action.')
    In the above example, what does value hold if an exception is raised?
  3. Catch any exception: vals = ['a', 'b', 'c', 'd']
    pos = input("Get the value at what position? ")
    try:
        pos = int(pos)
        value = vals[pos]
    except Exception:
        print('Unable to perform action.')
    Exception is the most general exception there is. Using it will catch any exception.
  4. Catch any exception: vals = ['a', 'b', 'c', 'd']
    pos = input("Get the value at what position? ")
    try:
        pos = int(pos)
        value = vals[pos]
    except:
        print('Unable to perform action.')
    If you don't specify the exceptions, it will catch any exception

The above examples show different ways to catch exceptions. The last two examples introduce the concept of exception hierarchy. We won't talk about exception hierarachies (unless we get to inheritance and have time), but basically some exceptions are more specific than others. The most general exception is Exception.

Exception hierarchies are important to be aware of when chaining together different except clauses. In the first example in the list above, we saw:

vals = ['a', 'b', 'c', 'd']
pos = input("Get the value at what position? ")
try:
    pos = int(pos)
    value = vals[pos]
except ValueError:
    print('Unable to convert "'+pos+'" into an integer.')
except IndexError:
    print('Invalid position in list.')

You could also have a "catch-all" except clause that caught any other exception, e.g.:

vals = ['a', 'b', 'c', 'd']
pos = input("Get the value at what position? ")
try:
    pos = int(pos)
    value = vals[pos]
except ValueError:
    print('Unable to convert "'+pos+'" into an integer.')
except IndexError:
    print('Invalid position in list.')
except Exception: #could also be written as just except:
    print('Unknown error occurred.')

Note that the order matters. If you have this instead, 'Unknown error occurred' will show for any exception that's raised:

vals = ['a', 'b', 'c', 'd']
pos = input("Get the value at what position? ")
try:
    pos = int(pos)
    value = vals[pos]
except Exception: #could also be written as just except:
    print('Unknown error occurred.')
except ValueError:
    print('Unable to convert "'+pos+'" into an integer.')
except IndexError:
    print('Invalid position in list.')

The exception handling environment starts with the first except clause and goes until it finds a match. It doesn't find the most specific match. It just finds the first match. Since Exception is the most general exception, ValueError and IndexError can both look like Exception. So, if you want a generic except clause, make sure it's last in the list of except clauses.

Exceptions as Objects

Exceptions are objects, just like everything else in Python. So, there are things you can do with them. But first, we need to figure out how to "get" an exception. In the above examples, we were just catching exceptions, but ignoring the object itself. To both catch and "get" and exception, use the as Python keyword:

try:
    print('a')
    int('13.8')
    print('this will never print')
except ValueError as ve:
    print('this will print because ValueError is raised.')

The as ve creates a variable called ve that holds the exception object. This variable will only exist within the except clause. Once that clause ends, the variable will be destroyed. But while you're in the exception clause, there are some things you can do with the variable. Most exceptions don't provide much information, but some do. What are some techniques for finding what an exception provides?

One thing you can do with all exceptions is print out their error message:

try:
    print('a')
    int('13.8')
    print('this will never print')
except ValueError as ve:
    print("Error:", ve) #this converts the object into a string

Exception Types

There are many exceptions provided by Python: https://docs.python.org/3/library/exceptions.html#bltin-exceptions. At this point, you don't need to know this list. You just need to know what kinds of exceptions your code may generate. In other words, know that int and float could throw ValueError exceptions, for example. Later, when we start writing functions, it would be good to be familiar with the exceptions.

Functions

Functions are sequences of instructions packaged together into a single statement. They can also be called "methods", "subroutines", "procedures", and "routines", depending on the programming language.

Why create functions?

<< Previous Notes Daily Schedule Next Notes >>