• No results found

Exception Handling

In document Python Programming for Starters (Page 186-189)

Decision Structures

7.4 Exception Handling

print("\nThe solutions are:", root1, root2 )

main()

7.4 Exception Handling

Our quadratic program uses decision structures to avoid taking the square root of a negative num-ber and generating an error at run-time. 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 the sqrt function.

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 root function should always return the non-negative root, this value could be used to signal that an error has 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 algorithm 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 successful, 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 the math.sqrt function:

# quadratic5.py import math def main():

print "This program finds the real solutions to a quadratic\n"

try:

178 Chapter 7. Decision Structures 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 ValueError:

print "\nNo real roots"

main()

Notice that this is basically the very first version of the quadratic program with the addition of a try...except around the heart of the program. A try statement has the general form:

try:

<body>

except <ErrorType>:

<handler>

When Python encounters a try statement, it attempts to execute the statements inside the body.

If these statements execute without error, control then passes to the next statement after the try...except. If an error occurs somewhere in the body, Python looks for an except clause with a matching error type. If a suitable except is found, the handler code is executed.

The original program without the exception-handling produced the following error:

Traceback (most recent call last):

File "quadratic.py", line 18, in <module>

main()

File "quadratic.py", line 12, in main discRoot = math.sqrt(b * b - 4 * a * c) ValueError: math domain error

The last line of this error message indicates the type of error that was generated, namely an ValueError. The updated version of the program provides an except clause to catch the ValueError.

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 the try...except statement 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

7.4. Exception Handling 179 bad set of coefficients. If the user fails to type the correct number of inputs, the program generates a different kind of ValueError. If the user accidentally types an identifier instead of a number, the program generates a NameError. If the input is not a valid Python expression, it generates a SyntaxError. If the user types in a valid Python expression that produces non-numeric results, the program generates a TypeError. A single try statement can have multiple except clauses 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 = eval(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 ValueError as excObj:

if str(excObj) == "math domain error":

print("No Real Roots") else:

print("You didn’t give me the right number of coefficients.") except NameError:

print("\nYou didn’t enter three numbers.") except TypeError:

print("\nYour inputs were not all numbers.") except SyntaxError:

print("\nYour input was not in the correct form. Missing comma?") except:

print("\nSomething went wrong, sorry!") main()

The multiple excepts are similar to elifs. If an error occurs, Python will try each except in turn looking for one that matches the type of error. The bare except at the bottom acts like an else and will be used if none of the others match. If there is no default at the bottom and none of the excepttypes match the error, then the program crashes and Python reports the error.

Notice how I handled the two different kinds of ValueErrors. Exceptions are actually a kind of object. If you follow the error type with an as <variable> in an except clause, Python will

180 Chapter 7. Decision Structures assign that variable the actual exception object. In this case, I turned the exception into a string and looked at the message to see what caused the ValueError. Notice that this text is exactly what Python prints out if the error is not caught (e.g., “ValueError: math domain error”). If the exception doesn’t match any of the expected types, this program just prints a general apology. As a challenge, you might see if you can find an erroneous input that produces the apology.

You can see how the try...except statement allows us to write bullet-proof programs. You can use this same technique by observing the error messages that Python prints and designing except clauses to catch and handle them. Whether you need to go to this much trouble depends on the type of program 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.

In document Python Programming for Starters (Page 186-189)