What Is An Exception?: - What Happens When Procedure Execution Hits An Unexpected Condition?
What Is An Exception?: - What Happens When Procedure Execution Hits An Unexpected Condition?
f = open(grades.txt)
# code to read and process grades
except:
raise Exception(Cant open grades file)
Types of exceptions
Already seen common error types:
SyntaxError: Python cant parse program
NameError: local or global name not found
AttributeError: attribute reference fails
TypeError: operand doesnt have correct type
ValueError: operand type okay, but value is illegal
IOError: IO system reports malfunction (e.g. file
not found)
finally:
Body of this clause is always executed after try,
else and except clauses, even if they raised
another error or executed a break, continue or
return
Useful for clean-up code that should be run no matter
what else happened (e.g. close file)
An example
def divide(x, y):
try:
result = x / y
except ZeroDivisionError, e:
print "division by zero! " + str(e)
else:
print "result is", result
finally:
print "executing finally clause"
An example, revised
def divideNew(x, y):
try:
result = x / y
except ZeroDivisionError, e:
print "division by zero! " + str(e)
except TypeError:
divideNew(int(x), int(y))
else:
print "result is", result
finally:
print "executing finally clause"
An example of exceptions
Here is an example of how we can use
exceptions to handle unexpected situations in
code
Assume we are given a class list for a subject:
each entry is a list of two parts a list of first
and last name for a student, and a list of
grades on assignments
We want to create a new subject list, with
name, grades, and a weighted average
A simple start
def getSubjectStats(subject, weights):
return [[elt[0], elt[1], avg(elt[1], weights)]
for elt in subject]
def dotProduct(a,b):
result = 0.0
for i in range(len(a)):
result += a[i]*b[i]
return result
def avg(grades, weights):
return dotProduct(grades, weights)/len(grades)
no grades data
[[['fred', 'flintstone'], [10.0, 5.0, 85.0], 15.5],
[['barney', 'rubble'], [10.0, 8.0, 74.0],
13.866666666666667], [['wilma', 'flintstone'], [8.0,
10.0, 96.0], 17.466666666666665], [['dino'], [], None]]
Note that last entry now has a None object for the average
grade
Example
def getRatios(v1, v2):
"""Assumes: v1 and v2 are lists of equal length of numbers
ReturnsL a list containing the meaningful values of
v1[i]/v2[i]"""
ratios = []
for index in range(len(v1)):
try:
ratios.append(v1[index]/float(v2[index]))
except ZeroDivisionError:
ratios.append(float('NaN')) #NaN = Not a Number
except:
raise ValueError('getRatios called with bad arg')
return ratios
Assertions
If we simply want to be sure that assumptions
on state of computation are as expected, we
can use an assert statement
We cant control response, but will raise an
AssertionError exception if this happens
This is good defensive programming
Example
def avg(grades, weights):
assert not len(grades) == 0, no grades data
newgr = [convertLetterGrade(elt) for elt in grades]
return dotProduct(newgr, weights)/len(newgr)
Example, extended
def avg(grades, weights):
assert not len(grades) == 0, no grades data
assert len(grades) == len(weights), wrong number grades
newgr = [convertLetterGrade(elt) for elt in grades]
result = dotProduct(newgr, weights)/len(newgr)
assert 0.0 <= result <= 100.0
return result
Example, extended
Slight loss of efficiency
Defensive programming:
by checking pre- and post-conditions on inputs
and output, avoid propagating bad values