Our quadratic program uses decision structures to avoid taking the square root of a negative number and generating a run-time error. This is a common pattern in many programs: using decisions to protect against
rare but possible errors.
In the case of the quadratic solver, we checked the data before the call to thesqrtfunction. Sometimes functions themselves check for possible errors and return a special value to indicate that the operation was unsuccessful. For example, a different square root operation might return a negative number (say -1) to indicate an error. Since the square roots of real numbers are never negative, this value could be used to signal that an error had occurred. The program would check the result of the operation with a decision.
discRt = otherSqrt(b*b - 4*a*c) if discRt < 0:
print "No real roots." else:
...
Sometimes programs become so peppered with decisions to check for special cases that the main algo- rithm for handling the run-of-the-mill cases seems completely lost. Programming language designers have come up with mechanisms for exception handling that help to solve this design problem. The idea of an exception handling mechanism is that the programmer can write code that catches and deals with errors that arise when the program is running. Rather than explicitly checking that each step in the algorithm was suc- cessful, a program with exception handling can in essence say “Do these steps, and if any problem crops up, handle it this way.”
We’re not going to discuss all the details of the Python exception handling mechanism here, but I do want to give you a concrete example so you can see how exception handling works and understand programs that use it. In Python, exception handling is done with a special control structure that is similar to a decision. Let’s start with a specific example and then take a look at the general approach.
Here is a version of the quadratic program that uses Python’s exception mechanism to catch potential errors in themath.sqrtfunction.
# quadratic5.py import math def main():
print "This program finds the real solutions to a quadratic\n" try:
a, b, c = input("Please enter the coefficients (a, b, c): ") discRoot = math.sqrt(b * b - 4 * a * c)
root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a)
print "\nThe solutions are:", root1, root2 except OverflowError:
print "\nNo real roots"
Notice that this is basically the very first version of the quadratic program with the addition of atry...except
around the heart of the program. Atrystatement has the general form:
try:
<body>
except <ErrorType>: <handler>
When Python encounters atrystatement, it attempts to execute the statements inside the body. If these statements execute without error, control then passes to the next statement after thetry...except. If an error occurs somewhere in the body, Python looks for anexceptclause with a matching error type. If a suitableexceptis found, the handler code is executed.
Traceback (innermost last): File "<stdin>", line 1, in ?
File "quadratic.py", line 13, in ?
discRoot = math.sqrt(b * b - 4 * a * c) OverflowError: math range error
The last line of this error message indicates the type of error that was generated, namely anOverflowError. The updated version of the program provides anexceptclause to catch theOverflowError.
Here is the error handling version in action:
This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 1,2,3
No real roots
Instead of crashing, the exception handler catches the error and prints a message indicating that the equation does not have real roots.
The nice thing about thetry...exceptstatement is that it can be used to catch any kind of error, even ones that might be difficult to test for, and hopefully, provide a graceful exit. For example, in the quadratic solver, there are lots of other things that could go wrong besides having a bad set of coefficients. If the user fails to type the correct number of inputs, the program generates aValueError. If the user accidently types an identifier instead of a number, the program generates aNameError. If the user types in a valid Python expression that produces non-numeric results, the program generates aTypeError. A singletry
statement can have multipleexceptclauses to catch various possible classes of errors.
Here’s one last version of the program designed to robustly handle any possible errors in the input.
# quadratic6.py import math def main():
print "This program finds the real solutions to a quadratic\n" try:
a, b, c = input("Please enter the coefficients (a, b, c): ") discRoot = math.sqrt(b * b - 4 * a * c)
root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a)
print "\nThe solutions are:", root1, root2 except OverflowError:
print "\nNo real roots" except ValueError:
print "\nYou didn’t give me three coefficients." except NameError:
print "\nYou didn’t enter three numbers" except TypeError:
print "\nYour inputs were not all numbers" except:
print "\nSomething went wrong, sorry!"
The multipleexcepts are similar to elifs. If an error occurs, Python will try eachexceptin turn looking for one that matches the type of error. The bareexceptat the bottom acts like anelseand will be used if none of the others match. If there is no default at the bottom and none of theexcepttypes match the error, then the program crashes and Python reports the error.
You can see how thetry...exceptstatement allows us to write really bullet-proof programs. Whether you need to go to this much trouble depends on the type of program that you are writing. In your beginning
programs, you might not worry too much about bad input; however, professional quality software should do whatever is feasible to shield users from unexpected results.