• No results found

Error Checking and Exceptions

In document OO Programming in Python (Page 197-200)

Additional Control Structures

5.5 Error Checking and Exceptions

In your experiences thus far, you have undoubtedly run across situations in which the Python interpreter informs you of an error. Looking back at our earlier chapters, we have seen aNameError on page 34, TypeError’s on pages 34 and 51, and ValueError’s on pages 36 and 47. Each of these types of standard errors is a subclass of a general Exceptionclass. In this section, we explore the role of exceptions in the language.

2. Proving such a guarantee relies upon international standards for floating-point implementations, as recommended by the IEEE organization.

Exceptions are used to report scenarios that are out of the ordinary, such as an attempt to use an identifier that is undefined, or a call to a function with the wrong number or type of parameters. Typically, the result of such an exception is to immediately stop the currently executing code. For a programmer, the exception describes the immediate cause of a significant problem. For a nonprogrammer who is the end user of software, seeing such a reported error is not helpful and reflects poorly on the quality of the software.

Consider the code from page 165, which prompts the user for a number from 1 to 10. At the time we used a while loop in an effort to ensure that a number typed by the user really is from 1 to 10. However, even that program is defective. If a user were to enter something other than an integer, for example banana, the following occurs:

Enter a number from 1 to 10: banana Traceback (most recent call last):

File "oneToTen.py", line 3, in

-toplevel-number = int(raw_input('Enter a -toplevel-number from 1 to 10: ')) ValueError: invalid literal for int(): banana

To someone familiar with Python programming, this error message has meaning. The problem occurred when executing the following line of our program:

number = int(raw_input('Enter a number from 1 to 10: '))

The call toraw_inputreturns the actual character string entered by the user, and the sep-arate call toint( )is used to convert that string to the corresponding integer. The problem is that the string 'banana' is not a valid literal for an integer. This gets reported as a ValueErrorbecause the value of the given string is not legitimate for this purpose.

While we are able to make sense of this error, such outward behavior should never be accepted in consumer-quality software. Abruptly quitting the program is an unreasonable reaction to an errant input. A more reasonable response is to remind the user that an integer is expected, and then to reprompt.

5.5.1 Catching an Exception

To design a more robust piece of software, we need to introduce a new control structure termed a try statement. It is used to encase a block of code containing one or more state-ments that might cause an exception. Informally, we refer to this as catching the exception.

The most straightforward syntax for the statement appears as follows:

try:

body

except errorClass:

recoveryBody

This code works as follows. The primary body of the try statement begins executing, and if all goes well it completes as normal. However, if the specified type of exception is raised during the execution of the body, the execution of the body is immediately interrupted and the appropriate commands from the recovery body are executed.

As an example, here is a new version of the program for reading an integer, this time taking care to respond gracefully when a noninteger string is entered by the user.

1 number = 0 # not valid

2 while not 1 <= number <= 10:

3 try:

4 number = int(raw_input('Enter a number from 1 to 10: ')) 5 if not 1 <= number <= 10:

6 print'Your number must be from 1 to 10.' 7 except ValueError:

8 print'That is not a valid integer.'

Let’s consider the flow of control for three possible scenarios shown in the following execution:

Enter a number from 1 to 10: 23 Your number must be between 1 an 10.

Enter a number from 1 to 10: banana That is not a valid integer.

Enter a number from 1 to 10: 9

During the first pass of the loop, the user enters 23. At line 4, this is converted (without error) to the corresponding integer number. Because this number is not within the desired range, the conditional at line 5 triggers the output at line 6. At this point, the body of the try statement has completed without error; therefore the except clause is irrelevant to this scenario. However, we are still within the context of the while loop body, so the condition at line 2 is reevaluated and because it fails, the while loop body is executed again.

During the second pass, the user enters the string banana. This causes aValueError to be raised by line 4 during the attempted conversion to a number. This error causes the immediate interruption of the try statement body. Thus we do not execute lines 5 and 6 in this scenario. Instead, control is taken to the body of the except clause in which we print a more appropriate error message. Still, we are within the context of the while loop body and so control proceeds back to the condition at line 2. As a technical note, when we executed line 4 in the previous scenario the error occurred before the assignment tonumber. So that variable still holds its preceding value (in this example, 23). Because that number is not in the desired range, the body of the while loop is entered again.

In the third pass, the user enters 9. This is correctly converted to a number at line 4.

Then the conditional of line 5 is evaluated. Since our new number is within the desired range, the conditional body is bypassed, bringing us to the end of the try body and also to the end of the while loop iteration. Once again, control is brought back to the while condition at line 2 and in this case the loop exits.

So our new version does a much better job of handling errors gracefully. Still it is not perfect. Although we seem to be prepared for any string that the user might enter, the user can cause our software to crash by typing the control-d keystroke when prompted for a response. This causes the following response.

Enter a number from 1 to 10: control-d Traceback (most recent call last):

File "oneToTen.py", line 4, in

-toplevel-number = int(raw_input('Enter a -toplevel-number from 1 to 10: ')) EOFError

Technically the problem is that the control-d keystroke closes the input console and so the call toraw_inputfails with anEOFError. When the error is raised the originaltry body is interrupted, and because this particular type of error was not caught by the except statement we again have an uncaught exception, resulting in the interruption of the overall program. In cases where different types of error are possible, we can provide multiple except clauses. However, in the case of anEOFError we cannot simply print an error message and reprompt, because the input console has already been closed; all subsequent raw_inputcalls would similarly fail. Instead, our design is to simply pick a number on the user’s behalf so that we can move on beyond the while loop. Our revised implementation is

number = 0 # not valid

while not 1 <= number <= 10:

try:

number = int(raw_input('Enter a number from 1 to 10: ')) if not 1 <= number <= 10:

print'Your number must be from 1 to 10.' except ValueError:

print'That is not a valid integer.' except EOFError:

print'Why did you do that?' print'We will choose for you.' number = 7

It is also possible to have a final exceptclause without any explicit designation for an exception type. That clause will be triggered by any exceptions that were not already handled.

5.5.2 Raising an Exception

In the preceding discussion, we saw how to gracefully catch errors that occur. We also have the ability to raise exceptions from within our own code. As a motivating example, we revisit the function for computing a square root (specifically, we use thesqrtDversion from Section 5.4 as our basis).

That code works properly so long as the caller sends a nonnegative value as a parame-ter; however, it malfunctions in the case where a negative number is sent, such assqrt(−5). While we might blame a caller who sends such a parameter, it still reflects poorly on us if our function returns an inaccurate result without drawing attention to the problem. This is a perfect situation for raising an exception, in particular aValueErrorwhich is the standard error used in a case where a parameter is of the correct data type but not a legitimate value of that type. A more robust version of this function appears as follows.

In document OO Programming in Python (Page 197-200)