Why use Exceptions? How-to use Exceptions in the correct way!

What will we cover in this tutorial?

The key to use exceptions correct is to understand why we use them.

The best way to understand why to use exceptions is to see what happens when we do not use exceptions in our code.

After that exploration we will show how it can solve the examples with exceptions.

Step 1: In the perfect world!

Remember those times before the exception was invented?

No? Well, of course not. It is long time ago, in a distant past in a programming language you probably have not heard about.

As an aspiring programmer, you have probably seen and heard about exceptions, but never put them to use yourself.

The best way is to create examples that can be greatly simplified with exceptions.

Let’s keep the world simple.

def add(a, b):
    return a + b


print(add(2, 3))

This would print out the result 5.

As long as the world is clean and everybody uses the function correct, there is nothing to worry about.

But what if someone calls the function with wrong types of arguments. Then the function does not do as expected.

def add(a, b):
    return a + b


print(add("2", "3"))

This will print 23, which might be a bit surprising if you are not familiar with the function, and possibly how Python uses addition on strings.

So how to handle it.

Step 2: In the real world where people do not use things as intended

As you will realize in your careers of programming, it happens that other programmers do not use your function correctly.

Why would they do that?

Maybe they don’t take the time to read the good documentation you wrote. Or maybe they are just careless and working under hard time pressure.

Even more funny, it could be you. It could be code you wrote a year ago (or less), and the documentation was not that good as you thought, hence, you use your own function incorrectly.

To continue the simple example. One way to handle wrong input without exceptions could be as follows.

def add(a, b):
    if type(a) is not int or type(b) is not int:
        return None
    return a + b


result = add("2", "3")
if result is not None:
    print(result)
else:
    print("Invalid input format to function add!")

Oh, no! What happened. Actually, your function is not that difficult to understand. It just starts by checking if the input format is as expected. If not, return an error code. As this is Python, an error code can simply be the value None.

That might seem fine. But what about the user of your function? Now the user needs to validate that the correct format of the result was returned.

This makes the code more complex. Also, the user of your function needs to know more details of how your function handles invalid input, or whatever can go wrong in your function.

The problem is, that you pass on a problem to someone else, which needs to know about the details of how your function works.

This is the opposite of decoupling. And decoupling is considered good. Easier to program, easier to change, easier to maintain, to mention a few benefits.

Step 3: Solve it with exceptions

The core idea of exceptions is to handle the when something out of the ordinary happens from the main logic of a program.

What? Yes, the expected case in our simple function, is that the user provides integers as input. But it might happen they do not call it with integers. That is unexpected, that is something out of the ordinary, and this breaks the logic in how you would build the program.

So how should we do in the above example?

def add(a, b):
    if type(a) is not int or type(b) is not int:
        raise Exception("Input format incorrect")
    return a + b

Actually, your part of the obligations becomes easier.

How?

Because now you do not need to write complex documentation of the error return codes in your program. The documentation is kept automatically in the exception. It will even guide the user to where in the code the exception comes from.

def add(a, b):
    if type(a) is not int or type(b) is not int:
        raise Exception("Input format incorrect")
    return a + b


result = add("2", "3")

This would give the following result.

Traceback (most recent call last):
  File "/Users/admin/PycharmProjects/LearningSpace/test2.py", line 7, in <module>
    result = add("2", "3")
  File "/Users/admin/PycharmProjects/LearningSpace/test2.py", line 3, in add
    raise Exception("Input format incorrect")
Exception: Input format incorrect

Which leads the user of the function to where in the code it went wrong. Then it will be easier for them to figure out what is wrong and how to fix it.

Step 4: Try-catch from the user

It happens that the user of your function would like to handle the special case.

They now master what is going wrong, and want to inform their users of the problem.

def add(a, b):
    if type(a) is not int or type(b) is not int:
        raise Exception("Input format incorrect")
    return a + b


try:
    result = add("2", "3")
except:
    print("The input format is incorrect")

This leads to something you might not notice at first. It leads to a good easy flow in your program. It is easy to read and understand.

It is clear that this is not intended flow in the except-part of the program. Also, the normal flow in the program would be in the try-part.

It makes it easier to understand, which is the ultimate goal of a good programmer. Make your code easy to read for others. That is, including you in less than 6 months (we do forget our own code and the logic in the programs we write faster than we expect).

Leave a Reply