Repetition in Python: Exceptions and Errors

Computers do not, as a general rule, make mistakes. Like other human-de­signed and constructed devices such as cars and stoves, computers can be awk­ward to use, can have design features that don’t turn out as expected, and can even break down too quickly. But they do not make mistakes. A computer program, on the other hand, almost certainly has mistakes or bugs coded within it. Consumers don’t usually make a distinction between the computer and the software that runs on it, but programmers and engineers must. When a computer program does not work properly, a programmer must exhaust all ways the program could be wrong before looking at an error in the computer itself.

Creating a correct program is difficult for many reasons. First, before any code is written, the problem to be solved must be clearly understood, and it must be the correct problem. Solving the wrong problem is a common error, but can’t be detected or corrected by the computer. Common examples of this sort of error come from stating the problem in English (or a human language of any description) where errors in understanding occur. “Find the average of the first ten integers,” for example, is a little ambiguous. Is the first integer 0 or 1? What is meant by average, the mean or the median? Computer programmers tend to be quite literal, and so what they think is the answer will be written into the code, and then they will argue for that answer as being correct. It is very important to realize that, whatever the literally correct answer is, the real correct answer is based on the correct understanding of the problem. Sometimes it is stated badly, but no matter whose fault the problem is, the job of fixing it lies with the programmer. Some­times a little time at the beginning clarifying the question can save more time later, and sticking with an overly pedantic interpretation will cause problems in the long run.

A correct program also depends on the programmer being able to identify all possible circumstances that can occur and knowing how to deal with each of them. Failing to handle one possible situation is an error, and the program will behave unpredictably if that situation occurs in practice. Statements that handle errors appear in real (in the field or commercial) code. In fact, it is common that there are more statements that detect and deal with errors than code that actually computes an answer. One thing that should be remembered: all lines of the code need to be tested. In very large programs this may be impossible, but every line of code that has never been executed is a potential error. Test as many as possible, including the error detection code.

User input is a frequent cause of mistakes in programs. It’s not that the user is the problem; the programmer must anticipate all possible ways that a user can enter data. There is usually one correct way but many erroneous ones, and it is impossible to predict what a user will enter from a keyboard in response to any request. Similarly, the contents of a file may not be what the programmer expects. File formats are standard, but sometimes there are variations and at other times a user may have entered the data improperly. While the mistake is on the part of the user, it is also a programming mistake if the error is not detected and is allowed to have an impact of the execution of the program.

Programmers tend to make assumptions about the problem. It is a common mistake to think “this situation can never happen” and then ignore it, however unlikely the situation seems. Testing every statement for everything that could possibly go wrong may be impossible, but testing for the general situation may be possible. It would be great to be able to say “if any statement in this section of code divides by zero,” or “if any variables in this code have the wrong type,” then do some particular thing.

Since it is impossible to write a program of any length without there being coding errors of some kind included, a step towards a solution may be to check all data before it is operated on to ensure the pending operation is going to suc­ceed. For instance, before performing the division a/b, test to make sure that b is not zero. This depends on the error being at least in principle predictable. Most modern languages, Python included, have implemented a way to catch errors and permit the programmer to handle them without having tests before each state­ment or expression. This facility is called the exception.

The word exception communicates a way to think about how errors will be handled. Some code is legal and calculates a desired value except under certain circumstances or unless some particular thing happens. The way it works is that the program tries to perform some operation and errors are allowed to occur. If one does, the computer hardware or operating system detects it and tells Python. The program cannot continue in the way that was planned, which is why this is called an exception. The programmer can tell Python what to do if specific errors occur by writing some code that deals with the problem. If the programmer did not do this, then the default is for Python to print an error message that describes the error and then stop executing the program. Error messages can be seen as a failure on the part of the programmer to handle errors correctly.

A simple example is the divide by zero error mentioned previously. If the expression a/b is to be evaluated, the value of b can be checked to make sure it is not zero before the division is done:

if b != 0:

c = a/b

This can be tedious for the programmer if a lot of calculations are being done and can be error prone. The programmer may forget to test one or two expres­sions, especially if engaged in modifications or testing. Using exceptions is a matter of allowing the error to happen and letting the system test for the problem. The syntax is as follows:

try:

c = a/b

except:

c = 1000000

The try statement begins a section of code within which certain errors are being handled by the programmer’s code. After that statement, the code is in­dented to show that it is part of the try region. Nearly any code can appear here, but the try statement must be ended before the program ends.

The except statement consists of the key word except and, optionally, the name of an error. The errors are named by the Python system, and the correct name has to be used, but if no error name is given as in this example then any error will cause the code in the except statement to be executed. Not specifying a name here is an implicit assumption that either only one kind of error could possibly occur or that no matter what error happens, the same code will be used to deal with it. Specifying an unrecognized name is itself an error. The name can be a variable, but that variable must have been assigned a recognized error name before the error occurs. The code following the except keyword is indented too, to show that it is part of the except statement. This is referred to by programmers as an error handler, and is executed only if the specified error occurs.

This appears to be even more verbose than testing b, but any number of state­ments can appear between the try and the except. This section of code is now protected from divide by zero errors. If any occur, then the code following the except statement is executed, otherwise that code does not execute. If other errors occur, then the default action takes place – an error message is printed.

Testing specifically for the divide by zero error can be done by specifying the correct error name in the except statement:

try:

c = a/b

except ZeroDivisionError:

c = 1000000

More than one specific error can be caught in one except statement:

try:

c = a/b

except (ValueError, ZeroDivisionError):

c = 1000000

Clearly (ValueError, ZeroDivisionError ) is a tuple, and could be made longer and assigned to a variable.

There can be many except statements associated with a single try:

try:

c = a/b

except ValueError:

c = 0

exceptZeroDivisionError:

c = 1000000

As was mentioned earlier, a variable can hold the value of the error to be caught:

k = ZeroDivisionError

try:

c = a/b

except k:

c = 1000000

Finally, the exception name can be left out altogether. In that case, any excep­tion that occurs will be caught and the exception code will be executed:

try:

c = a/b

except:

c = 0

1. Problem: A Final Look at Guess a Number

The final version of the program involving guessing a number looks like this:

choice = 7

print (“Please guess a number between 1 and 10:       “)

playerchoice = int(input())

if choice == playerchoice:

print (“You win!”)

else:

print (“Sorry, you lose.”)

Using exceptions and what has been discussed about error checking, this program can be improved. First, if the user enters something that is not an integer, it is an error. This should be caught using an exception. Rather than forcing the player to run the program again, a loop can be used to ask for another guess. The input should be within the try statement. The except statement should print an error message, and the entire collection should be within a loop that continues to ask the user to guess a number. Here is a better version:

choice = 7

guessed = False # Has the user guessed a reasonable num­ber?

while not guessed: # Keep trying until they have

print (“Please guess a number between 1 and 10:    “)

try:           # Catch potential input errors

playerchoice = int(input())

guessed = True # Success so far

except:        # An error occurred.

print (“Sorry, your guess must be an integer.”)

if choice == playerchoice:   # Correct guess?

print (“You win!”)

else:

print (“Sorry, you lose.”)

The variable guessed is set to True when a successful guess is made, and this stops the loop from repeating. If the user enters a real number or a string, the exception is caught before that happens, the error message is printed, and the user is asked to enter another guess.

What else is wrong with this code? The user is asked to enter a number be­tween 1 and 10, but that value is never checked to see if it is valid. If it falls out­side the range, then it will always be an incorrect guess and the player will lose. It’s a penalty for not paying attention to the rules. A program should give the user as much information as is reasonable, so it would be better to check the value of the variable playerchoice and give an error message if it is out of range. The best way to do this is to place the check after the except statement at the bottom of the loop, and set the variable guessed to False if the guess is an improper one. Then the loop will repeat and the player will get another guess.

This version of the program is as follows:

choice = 7

guessed = False

while not guessed:

print (“Please guess a number between 1 and 10:    “)

try:

playerchoice = int(input())

guessed = True

except:

print (“Sorry, your guess must be an integer.”)

if playerchoice<10 or playerchoice>10: # Is the guess

                                            # in 1..10?

print (“Your guess was”,playerchoice,

“which is out of range.”)

guessed = False                   # Nope. Guess again

if choice == playerchoice:

print (“You win!”)

else:

print (“Sorry, you lose.”)

 

Source: Parker James R. (2021), Python: An Introduction to Programming, Mercury Learning and Information; Second edition.

Leave a Reply

Your email address will not be published. Required fields are marked *