Part Seven: Error Handling
Tags: coding, python, programming, tutorial
Published: 2022-12-05 09:00:00


> "There are two ways to write error-free programs; only the third one works." - _Alan Perlis_

---


Welcome to part seven. In this series I hope to introduce the basics of coding in Python for absolute beginners in an easy to follow and hopefully fun way. In this article we will look at writing code once but being able to use it as many times as we like without writing it out every time, but first let's take a look at the solution for the challenge set in part six.

---

**Solution to part six's challenge**

---
Here are a couple ways this could be accomplished. If your method was different but still worked - congratulations! If you struggled, have a look at the examples below and if you have time, have another go at it.

Example 1 - putting the whole thing inside a while loop
```
run = True

while run == True:

    def add(num1, num2):
        result = num1 + num2
        print(result)

    def sub(num1, num2):
        result = num1 - num2
        print(result)

    def div(num1, num2):
        result = num1 / num2
        print(result)

    def mul(num1, num2):
        result = num1 * num2
        print(result)

    choice = input("Enter 'add', 'sub', 'div' or 'mul'")

    number1 = input("Enter first number: ")
    number1 = int(number1)
    number2 = input("Enter second number: ")
    number2 = int(number2)

    if choice == 'add':    
        add(number1, number2)
    elif choice == 'sub':
        sub(number1, number2)
    elif choice == 'div':
        div(number1, number2)
    elif choice == 'mul':
        mul(number1, number2)
    else:
        print("Choice not recognised, please run application again")
    
    run_again = input("Perform another calculation? ")
    if run_again == 'y' or run_again == 'Y':
        run = True
    else:
        run = False
```
Example 2 - using recursion
```
def calculator():
    def add(num1, num2):
        result = num1 + num2
        print(result)

    def sub(num1, num2):
        result = num1 - num2
        print(result)

    def div(num1, num2):
        result = num1 / num2
        print(result)

    def mul(num1, num2):
        result = num1 * num2
        print(result)

    choice = input("Enter 'add', 'sub', 'div' or 'mul'")

    number1 = input("Enter first number: ")
    number1 = int(number1)
    number2 = input("Enter second number: ")
    number2 = int(number2)

    if choice == 'add':    
        add(number1, number2)
    elif choice == 'sub':
        sub(number1, number2)
    elif choice == 'div':
        div(number1, number2)
    elif choice == 'mul':
        mul(number1, number2)
    else:
        print("Choice not recognised, please run application again")
    
    run_again = input("Perform another calculation? ")
    if run_again == 'y' or run_again == 'Y':
        calculator()
    else:
        pass

calculator()
```

---

> "Any fool can use a computer. Many do." - _Ted Nelson_

---

**User Input Problems**

---
All being well, you should have an application which can take input from a user, perform simple calculations, and run multiple times as needed.

Perhaps you came across this issue when you were writing your code but if not consider the following situation. Where we ask for user input, for example, the first and second numbers, what if the user didn't enter a number but a string instead? What if they inputted a boolean value or maybe no value at all and simply pressed enter.

Unfortunately, whether accidentally or intentionally, users won't always do what we think they will. Therefore, we need to try and anticipate potential pitfalls and put code in place to avoid crashing our applications.

---
**Error Handling - reducing the chance of an incorrect input**

---

Let's work through our calculator example and see where we can anticipate users causing problems. For the remainder of this article I will be referring to my first example but feel free to adapt this to your own solutions.

We will start at line number 21 in my example where we ask the user to choose a type of calculation to perform:
```
choice = input("Enter 'add', 'sub', 'div' or 'mul'")
```
This will work well as long as the user types exactly what we have asked for. However, a user wanting to perform addition may type 'Add', 'ADD', or make a typo such as 'ads'. This input is stored in a variable which is compared in an if / else statement starting on line 28. Anything other than 'add' here will throw an error.

So how can we overcome this? The first possible step would be change what we are asking for. Instead of asking the user to type a string of 3 characters which they may misspell, we could simply ask for a number:
```
choice = input("Enter 1 to add, 2 to subtract, 3 to divide or 4 to multiply")
```
Now the user only has to enter one character which reduced the chances of errors. 

We could also use a letter instead of a number:
```
choice = input("Enter 'A' to add, 'S' to subtract, 'D' to divide or 'M' to multiply")
```
However, this raises the issue of did the user use upper or lower case? To solve this, we can convert any letter the user gives us to upper case, before the if statement looks at it, to ensure no matter what is typed it will be upper case. To do this we use a built in Python function - `upper()`. The upper method is technically a method and not a function but we will get into methods in another article. Here's how we use it, we can call upper.() on a variable by using it after a '.' at the end of the variable name:
```
string_variable = "goose"
string_variable.upper()
print(string_variable)

# Output
'GOOSE'
```
So in our calculator example, we simply add `upper()` to the end of our choice input line to convert the input to upper case:
```
choice = input("Enter 'A' to add, 'S' to subtract, 'D' to divide or 'M' to multiply").upper()
```

---
**Converting Data Types**

---

Moving on to line 23, we already have some error handling in place here. Because the input() function returns a string it means that we cannot take number inputs from it directly. When the user gives the input - 23 as a number, the input function records this as the string "23". This is no good for performing calculations as we can only do this with integers or floats.

To solve this, we used the `int()` function to convert the string from the user input into an actual integer we can use in calculations:
```
number1 = input("Enter first number: ")
number1 = int(number1)
```
However, what if the user wanted to calculate decimal numbers? Inputting a decimal number currently will throw a value error as we are trying to convert a float to an integer. 

While we could examine the float to try and see if it contains a '.' and then use flow control to then convert to an integer or a float depending on the result.....it's just easier to convert everything to a float and work solely in decimal numbers:
```
number1 = input("Enter first number: ")
number1 = float(number1)
```
Now decimal numbers can be handled without issue and any integers can still be used but will just be displayed with '.0' after them.

---
**Using Functions and Flow Control**

---

The final method for error handling I am going to look at here is using functions and flow control to account for anything the user throws at you which you did not account for.

In our example, we have some flow control which checks the `choice` variable against some criteria to determine the calculation type to be performed. We check for predetermined answers but if anything else is found, we exit the flow control and proceed to ask if the user wishes to run the application again. In addition, regardless of what the user enters, it is not validated until after they have entered their 2 numbers.

One way of solving this is to re-write our code into functions and then call them as needed. This will require us to re-arrange or 're-factor' our code so take your time and go through line by line if you have any issues understanding what is happening. 

First we take our code which asks the user for 2 numbers and make it into a function which returns those numbers:
```
def get_nums():
        number1 = input("Enter first number: ")
        number1 = int(number1)
        number2 = input("Enter second number: ")
        number2 = int(number2)
        return number1, number2
```
Now, we take the flow control code which assesses which calculation we wish to perform and make another function which can call itself if we cannot validate the user's input. We can cut and paste our user input line from above and put it at the start of this function. Now the calculation choice input is validated before we get the user's numbers:
```
def calc_choice():
        choice = input("Enter 'A' to add, 'S' to subtract, 'D' to divide or 'M' to multiply").upper()
        if choice == 'A':
            num1, num2 = get_nums()    
            add(num1, num2)
        elif choice == 'S':
            num1, num2 = get_nums()    
            sub(num1, num2)
        elif choice == 'D':
            num1, num2 = get_nums()    
            div(num1, num2)
        elif choice == 'M':
            num1, num2 = get_nums()    
            mul(num1, num2)
        else:
            print("Choice not recognised, please run application again")
            calc_choice()
```
Finally, now that all of our functions have been defined, we need to call our `calc_choice()` function to get things started. We must remember that a function must be defined before it can be called so we must make sure our code is arranged appropriately.

Here is what this would look like all together. We will go through it to explain how it works:
```
run = True

while run == True:

    def add(num1, num2):
        result = num1 + num2
        print(result)

    def sub(num1, num2):
        result = num1 - num2
        print(result)

    def div(num1, num2):
        result = num1 / num2
        print(result)

    def mul(num1, num2):
        result = num1 * num2
        print(result)

    def get_nums():
        number1 = input("Enter first number: ")
        number1 = float(number1)
        number2 = input("Enter second number: ")
        number2 = float(number2)
        return number1, number2

    def calc_choice():
        choice = input("Enter 'A' to add, 'S' to subtract, 'D' to divide or 'M' to multiply").upper()
        if choice == 'A':
            num1, num2 = get_nums()    
            add(num1, num2)
        elif choice == 'S':
            num1, num2 = get_nums()    
            sub(num1, num2)
        elif choice == 'D':
            num1, num2 = get_nums()    
            div(num1, num2)
        elif choice == 'M':
            num1, num2 = get_nums()    
            mul(num1, num2)
        else:
            print("Choice not recognised, please run application again")
            calc_choice()
    
    calc_choice()

    run_again = input("Perform another calculation? ")
    if run_again == 'y' or run_again == 'Y':
        run = True
    else:
        run = False
```
Here is how this application will run:
1. Line 1: The variable `run` is created and set to True
2. Line 3: A while loop looks at `run` and if it is True runs its indented code
3. Lines 5-44: 5 functions are defined
4. Line 46: The `calc_choice()` function is called and runs its code
5. The user is asked for input 'A','S','D' or 'M' and this is stored in the variable `choice` after being converted to upper case
6. Flow control looks at the value of `choice` and if it matches one of the set conditions `get_nums()` is called and the returned values stored in variables called `num1` and `num2`
7. The appropriate calculation function is called using `num1` and `num2` as arguments
8. If `choice` does not meet any of the set conditions, the else statement runs and calls `calc_choice()` again. Go back to step 5
9. If a calculation has been performed we move to line 48 where the user is asked if they want to go again
10. Flow control compares the user's answer to step 9. If the answer is 'y' or 'Y' then the while loop from step 2 runs again. If the answer is ANYTHING else, `run` from step 1 is set to False and the loop will not run again causing the application to exit

---
**Final Tweaks**

---

The only other thing we could do with this code is to shorten the final bit of flow control using the `upper()` method:
```
run_again = input("Perform another calculation? ").upper()
    if run_again == 'Y':
        run = True
    else:
        run = False
```
All together, this would look like this:
```
run = True

while run == True:

    def add(num1, num2):
        result = num1 + num2
        print(result)

    def sub(num1, num2):
        result = num1 - num2
        print(result)

    def div(num1, num2):
        result = num1 / num2
        print(result)

    def mul(num1, num2):
        result = num1 * num2
        print(result)

    def get_nums():
        number1 = input("Enter first number: ")
        number1 = float(number1)
        number2 = input("Enter second number: ")
        number2 = float(number2)
        return number1, number2

    def calc_choice():
        choice = input("Enter 'A' to add, 'S' to subtract, 'D' to divide or 'M' to multiply").upper()
        if choice == 'A':
            num1, num2 = get_nums()    
            add(num1, num2)
        elif choice == 'S':
            num1, num2 = get_nums()    
            sub(num1, num2)
        elif choice == 'D':
            num1, num2 = get_nums()    
            div(num1, num2)
        elif choice == 'M':
            num1, num2 = get_nums()    
            mul(num1, num2)
        else:
            print("Choice not recognised, please run application again")
            calc_choice()
    
    calc_choice()

    run_again = input("Perform another calculation? ").upper()
    if run_again == 'Y':
        run = True
    else:
        run = False
```
Using the second example I gave at the start we could also solve this with recursion:
```
def calc_app():

    def add(num1, num2):
        result = num1 + num2
        print(result)

    def sub(num1, num2):
        result = num1 - num2
        print(result)

    def div(num1, num2):
        result = num1 / num2
        print(result)

    def mul(num1, num2):
        result = num1 * num2
        print(result)

    def get_nums():
        number1 = input("Enter first number: ")
        number1 = float(number1)
        number2 = input("Enter second number: ")
        number2 = float(number2)
        return number1, number2

    def calc_choice():
        choice = input("Enter 'A' to add, 'S' to subtract, 'D' to divide or 'M' to multiply").upper()
        if choice == 'A':
            num1, num2 = get_nums()    
            add(num1, num2)
        elif choice == 'S':
            num1, num2 = get_nums()    
            sub(num1, num2)
        elif choice == 'D':
            num1, num2 = get_nums()    
            div(num1, num2)
        elif choice == 'M':
            num1, num2 = get_nums()    
            mul(num1, num2)
        else:
            print("Choice not recognised, please run application again")
            calc_choice()
    
    calc_choice()

    run_again = input("Perform another calculation? ").upper()
    if run_again == 'Y':
        calc_app()
    else:
        pass

calc_app()
```
Again, take your time here and go through line by line until you have a good handle on what is happening. If your solution to the challenge was significantly different from mine, try applying error handling to your own code to see how you can avoid users catching you out.

---

**Challenge**

---
The challenge this time is not really a challenge. We have come a long way since part one, you have learned data types, data structures, flow control, loops, functions, and now error handling. My hope is that you are now starting to think of your own projects and maybe you have already started some of them. For now I would like you to consolidate your coding knowledge and go back through everything you have learned. If you have the time, try a simple project or 2 to help you understand all of the concepts and how they work together. If you have come this far, you have gone from crawl to walk and now you are starting to run. Remember what sparked your interest in coding to begin with and keep it fun.

---
**Conclusion**

---
We have covered some difficult subject matter so far. Some of these concepts can be difficult for a beginner to understand (I know some are definitely hard to explain). I hope if you have struggled or are struggling, that you persist and stick with it as the rewards will be numerous when you have that 'eureka!' moment.

What I am building towards with this series is to get you to a point where you can write modular, object orientated code which will enable you to contribute to your field and community or write useful software for your daily life. You won't be a professional developer by the end, but you don't need to be, you work in land based industry not Facebook. 

The concepts get harder as we go from here and I will do my best to explain them as clearly as I can. However, if it doesn't click right away do not be disheartened, it's perfectly normal. Keep referring to as many sources as needed until you find the one that explains it in the right terms, we all learn differently.

The next article will look at classes and objects, and then on to some object orientated programming principles. This marks the end of what could be considered 'beginner' coding so if you have got this far give yourself a well deserved pat on the back!

Thank you for reading, constructive criticism is always welcome. I look forward to seeing you next time.

Simon.