Want to keep learning?

This content is taken from the Raspberry Pi Foundation & National Centre for Computing Education's online course, Programming Pedagogy in Secondary Schools: Inspiring Computing Teaching. Join the course to learn more.

Skip to 0 minutes and 2 seconds Through testing, you’ve managed to identify several errors, but it’d be helpful if you could catch these errors, called exceptions, and handle them without halting your program, or at least display a more meaningful error message. Defensive programming is a method designed to trap exceptions and allow your program to continue working without crashing. Whilst this step may be beyond the scope of key stage three, it is a useful technique to have available to help stretch your more able students. Let’s start by looking at a simplified version of our rock paper scissors game, and try and reproduce some exceptions. At the moment, we have a list of items. We have rock at position 0, paper at position 1, and scissors at position 2.

Skip to 0 minutes and 39 seconds So let’s try and put a number in that doesn’t exist in my list. If I put in the number 5 and hit enter, we come up with an error message that says index error, which is telling me that I don’t have a position 5 on my list. We also have a line of code that wants an input, but only wants it as an integer. So we try and put in a string instead of integer, and see what error message might occur.

Skip to 1 minute and 3 seconds So now we have a value error, which is telling me that it needs an integer and not a string. So we’re going to handle this by putting in a try. So when we type in the word try and colon, we want to indent the two lines of code that we wanted to try without an error happening. But we also need to tell it what to do if an error does happen, and this is called an exception. So we’re going to except pretty much any error message that appears, and we’ll put a print statement that says, oops, something has gone wrong.

Skip to 1 minute and 36 seconds So we run it now with the same data and put a number 5. Instead of the program crashing, we now have an error message that we have customised that tells us that something has gone wrong. We had two different types of errors. We had an index error and the value error. So let’s try and handle them separately, and give customised error messages depending on the error. So after except, I’m going to put in the word index error and change my message to tell my user to only put in a number that’s between 0 and 2.

Skip to 2 minutes and 13 seconds So if I try and run it again with my number 5, it tells me to only put a number in between 0 and 2. We also had a value error. So again, underneath it, making sure I’m not indented, I’m going to put except a value error. And this time, I’m going to change my error message so that it says, please enter a number and not text. So we try running this one, and type in abc. Again, our program hasn’t stopped, but what it has done is it’s printed out my customised error message. The article below outlines the process of creating a new subroutine for your rock paper scissors game that will handle the user input separately to your round subroutine.

Skip to 2 minutes and 58 seconds This will make your code easier to read and debug, using try statements to handle possible exceptions. Give it a go and share your experiences in the comments section. And be sure to help out your fellow learners if they get stuck. Good luck.

Defensive Programming

You’ve managed to identify several errors by testing, but it would be helpful if you could catch these errors (called exceptions) and handle them without halting your program. At the very least, you could display a more meaningful error message. This is known as defensive programming, and is a method of trapping exceptions and allowing your program to continue working without crashing. This step may be beyond the scope of Key Stage 3, but it is a useful technique to have available to stretch more able learners.

Analysing the errors

Start by seeing if you can reproduce some of the exceptions by entering invalid data into your program. Run this Trinket with the aim of deliberately causing these exceptions to occur.

When you receive an error message, it’s important to note the type of error, as you can make your program respond differently to each. For example, the following inputs produce two different errors: an “IndexError” and a “ValueError”.

Input data Error
5 IndexError: list index out of range
abc ValueError: invalid literal for int() with base 10

Exception handling

You can handle these exceptions by preceding some “risky” code with a try statement:

try:
    player = int(input("--> "))

except:
    print("Oops, the computer didn’t like that input")

The code above will “try” to run the line player = int(input("--> ")). If any exception occurs, rather than throwing up a message and stopping the code from running, the code underneath the except statement will run instead.

Using exception handling, you can be a bit more specific about your exceptions and give different messages to the user depending on what error has occurred, by including the error type after the word except. It would be more useful to handle your two types of error separately, as the feedback you give to the user might be different in each case. For example, if they enter text, you can tell then to enter a number; if they enter a value that is a number but isn’t within the range, you can remind them what the range is.

Adding exception handling to your rock, paper, scissors game

You’re now going to create a new subroutine that will handle the user input separately to your round()subroutine. Again, this will make our code easier to read and debug.

Start by creating a new subroutine above your round() subroutine:

def player_input():

Your intention here is to catch the error and not allow the user to continue unless they put in valid input. You’ll add a while loop into your new subroutine and start by placing the print statements from your round() subroutine into your player_input() subroutine:


def player_input():

    while True:

        print("Type 0 for rock")
        print("Type 1 for paper")
        print("Type 2 for scissors")

The next step is to add your try statement underneath these print statements.

        try:
            player = int(input("--> "))
            player_choice = rps_choices[player]
            return player_choice

Notice that again you have taken two more lines of code from your round() subroutine, but you have added a line that returns a value. This will therefore now try to execute the three lines of code beneath it.

The first line of code in your round() subroutine should now look as follows:


player_choice = player_input()

You might have noticed that we have two variables named player_choice that exist in your round() and player_input() subroutines. As they exist in their own subroutines, they don’t affect each other. You may want to rename one of them to avoid confusion.

Except

Finally, you need to go back to your player_input() subroutine and add your except blocks. You have two possible exceptions to handle: a ValueError and an IndexError. You can handle these errors separately by writing two different except statements, each time including the type of error after except. As usual, indent your code to show it belongs to the except statement above it.

Add two separate except blocks under your try statement and customise the error messages to explain how to avoid that type of error.

Your player_input() subroutine should now look like this:

def player_input():

    while True:

        print("Type 0 for rock")
        print("Type 1 for paper")
        print("Type 2 for scissors")

        try:
            player = int(input("--> "))
            player_choice = rps_choices[player]
            return player_choice

        except ValueError:
            print("Oops, looks like you entered text instead of a number. Please enter a whole number between 0 and 2")

        except IndexError:
            print("Oops, looks like you entered an incorrect number that wasn't a whole number between 0 and 2")

As you saw earlier in this step, it is also possible to simply use the word except and not be specific about the error, as follows. It can be good to use this to catch any errors that you haven’t thought of.


except:
  print("An error occurred")

This doesn’t necessarily help the user understand what they did wrong, but it does stop your program from crashing if an unexpected error occurs.

You can read more about exceptions in the Python documentation.

Discuss

  • From your experience, can you think of any situations other than user input where exception handling could be used?

At the end of this step your code could look similar to this Trinket.

Share this video:

This video is from the free online course:

Programming Pedagogy in Secondary Schools: Inspiring Computing Teaching

Raspberry Pi Foundation

Get a taste of this course

Find out what this course is like by previewing some of the course steps before you join: