It's easy to think of exceptions as a boring topic. Indeed, error checking is not terribly interesting, except when you actually want to make sure that your code works when it's supposed to. Anytime you've had a program crash while you were using it you've probably wished there was a way to prevent that crash from happening.
That's exactly what extensions do!
Don't get your hopes up too much just yet. Exception handling code cannot prevent all crashes for a number of reaons. For instance, not all problems can be predicted and checked for, and sometimes the user does something that you couldn't prepare for, such as kill your program while it is running. Sometimes the best you can do with exception handling is just exit gracefully. And that's what they are really there for. Exceptions aren't the magic bullet that makes all design problems with your program disappear, or prevent your program from ever crashing, but they enable your code to exit cleanly if errors occur, and help your code become a nice digital denizen.
Sorry about that. First off let's write a simple program with some flaws. This program needs input from the user. And we all know that the "user" is out to get us a crash our program, so we'll try to use exceptions to deal with our input.
Here's the program without exceptions:
#simplesieve.py
import Numeric
def sieve( n ):
# Let's start at array index 1 instead of 0
array = Numeric.zeros(n+1)
for i in xrange(1,n+1):
array[i] = i
done = False
prime = array[2] # The number, as well as the index.
while( prime < n ):
# Index 2 corresponds to the number 2 for this indexing scheme
index = prime
index += prime
# Mark multiples of prime
while( index <= n ):
array[index]=-1
index += prime
# Get a new prime
prime+=1
while( prime < n ):
if (array[prime]==-1):
prime+=1
elif (prime < n ):
break
#print prime,n,array
# Grab all the primes
primes = []
for i in xrange(1,n+1):
if( array[i] != -1):
primes.append(array[i])
return primes
def main():
print "This program computes the number of primes between 1 and n\nusing the Sieve of Erastothenes."
input = raw_input("Enter (n):")
n = int(input)
primes = sieve( n )
print "There are %d primes between 1 and %d. They are:" % (len(primes),n)
print primes
return
if __name__=="__main__":
main()
Now there are a number of ways that this program could fail. For instance, the user could enter a string, like there name, or their birthday or something. A negative number would cause the program to crash when it attempts to allocate a Numeric array of negative size. There are less obvious but still important problems like what happens if the Numeric library isn't available or isn't working on your users system?
This next version uses exception handling to fix some of these problems. Notice that we havne't gotten into raising our own exceptions yet. That's something we'll do a little later.
#simplesieve.py
import traceback
try:
import Numeric
except ImportError:
print "Sorry, you don't have the Numeric module installed, and this"
print "script relies on it. Please install or reconfigure Numeric"
print "and try again."
def sieve( n ):
# Let's start at array index 1 instead of 0
try:
array = Numeric.zeros(n+1)
except ValueError:
traceback.print_exc()
return []
for i in xrange(1,n+1):
array[i] = i
done = False
prime = array[2] # The number, as well as the index.
while( prime < n ):
# Index 2 corresponds to the number 2 for this indexing scheme
index = prime
index += prime
# Mark multiples of prime
while( index <= n ):
array[index]=-1
index += prime
# Get a new prime
prime+=1
while( prime < n ):
if (array[prime]==-1):
prime+=1
elif (prime < n ):
break
#print prime,n,array
# Grab all the primes
primes = []
for i in xrange(1,n+1):
if( array[i] != -1):
primes.append(array[i])
return primes
def main():
print "This program computes the number of primes between 1 and n\nusing the Sieve of Erastothenes."
input = raw_input("Enter (n):")
try:
n = int(input)
except ValueError:
print "You entered:",input
print "Please enter a positive integer next time."
return
primes = sieve( n )
print "There are %d primes between 1 and %d. They are:" % (len(primes),n)
print primes
return
if __name__=="__main__":
try:
main()
except:
print "An unhandled exception occured, here's the traceback!"
traceback.print_exc()
Notice the various ways you can handle errors with exceptions. You can simply return default values if something goes wrong as I do at the beginning of the sieve method, or you can have a more general error message to the user like I have in main. Notice the exceptions handling code wrapped around the call to main and the import Numeric statement. These are common and useful techniques to both document and check for errors.
In serious code it becomes useful to create your own exceptions for all sorts of things. Sometimes exceptions are used simple to transfer control from one place in the code to another, just like branch. Sometimes they are used just to deal with specific error conditions within the code itself.
Here's an example showing how to define and raise a simple exception. More serious use of the exceptions will be postponed for later discussion. Please send me an e-mail if you are curious about using exceptions in this way.
# myexception.py import exceptions class Expletive(exceptions.Exception): def __init__(self): return def __str__(self): print "","An Expletive occured!" def main(): raise Expletive if __name__=="__main__": try: main() except ImportError: print "Unable to import something..." except Exception, e: raise e