Handling errors: the try - except block

Contents

Handling errors: the try - except block #

If exceptions are not handled properly, the application will crash during execution as we have seen in the examples above. As a result, handling exceptions is important, because we want to minimise application crashes. Sometimes this is called “catching” errors.

It is important to also think about when we should catch errors: this is when you expect the error to be recoverable. In other words, dealing with isthe error should not affect future code that will run. Some errors are fatal and should be left to crash the program since things won’t work out anyways.

To handle exceptions, we can make use of try - except block, where we can catch exceptions and handle them in the way we wish. Thus, if an exception is raised, the application is not going to crash.

Let’s see how that works by reusing the function from the previous section:

def calculate_weight(density, volume):
    """
    Calculates weight of an object, given its density and volume.

    Args:
        density (int): Density of an object.
        volume (int): Volume of an object.

    Returns:
        int: Weight of an object.

    Raises:
        ValueError: if density or volume are negative values.
    """
    if density < 0:
        raise ValueError(
            "Invalid density. Density parameter can only take positive numbers"
        )
    if volume < 0:
        raise ValueError(
            "Invalid volume. Volume parameter can only take positive numbers"
        )
    return density * volume
weight = 0
density = -40
volume = 5

try:
    weight = calculate_weight(density, volume)
except ValueError:
    print("An exception occurred!")
An exception occurred!

Note that if we do not handle the correct exception, one will still be raised. Therefore, it is very important that we catch the correct one based on our knowledge of which error can occur.

weight = 0
density = -40
volume = 5

try:
    weight = calculate_weight(density, volume)
except NameError:
    print("An exception occurred!")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[15], line 6
      3 volume = 5
      5 try:
----> 6     weight = calculate_weight(density, volume)
      7 except NameError:
      8     print("An exception occurred!")

Cell In[11], line 16, in calculate_weight(density, volume)
      2 """
      3 Calculates weight of an object, given its density and volume.
      4 
   (...)
     13     ValueError: if density or volume are negative values.
     14 """
     15 if density < 0:
---> 16     raise ValueError(
     17         "Invalid density. Density parameter can only take positive numbers"
     18     )
     19 if volume < 0:
     20     raise ValueError(
     21         "Invalid volume. Volume parameter can only take positive numbers"
     22     )

ValueError: Invalid density. Density parameter can only take positive numbers

If we do not know what exception will be raised, we can use except Exception, which will handle any exception raised by the code inside the try clause block. (Exception is the super class that comprises all types of exceptions and errors; for the purpose of this activity, think of it as a generic error.):

weight = 0
density = -40
volume = 5

try:
    weight = calculate_weight(density, volume)
except Exception:
    print("An exception occurred!")
An exception occurred!

else and finally#

There are 2 more clauses that we can add to our try-except block, namely else and finally:

  • else will be executed only if no exception is raised by the code in the try clause block. This is useful in case we want some code to be executed after successful completion of the try clause. For example, printing a message or sending a response to somebody.

  • finally will be executed all the time, regardless of whether an exception was raised or not. In practice this is mostly used to finish a process. For example, if you are reading from a file, it is customary to close the file when you finish reading from it.

Study the example below, which shows the execution of except and finally clauses:

weight = 0
density = -40
volume = 5

try:
    weight = calculate_weight(density, volume)
except Exception:
    print("An exception occurred!")
else:
    print(
        f"The weight of an object with density of {density} kg/m^3 and "
        f"volume of {volume} m^3 is {weight} kg."
    )
finally:
    print("Calculation finished!")
An exception occurred!
Calculation finished!

We will now use valid input to our function calculate_weight to show the execution of try and else clauses:

weight = 0
density = 7870
volume = 40

try:
    weight = calculate_weight(density, volume)
except Exception:
    print("An exception occurred!")
else:
    print(
        f"The weight of an object with density of {density} kg/m^3 and "
        f"volume of {volume} m^3 is {weight} kg."
    )
finally:
    print("Calculation finished!")
The weight of an object with density of 7870 kg/m^3 and volume of 40 m^3 is 314800 kg.
Calculation finished!

It is also possible to interact with any exception object. Have a look at the example below, where e is the object of the raised exception:

weight = 0
density = -40
volume = 5

try:
    weight = calculate_weight(density, volume)
except ValueError as e:
    print(f"An exception occurred: {e}")
An exception occurred: Invalid density. Density parameter can only take positive numbers

It is possible to have multiple except statements in case you are expecting more than 1 type of exceptions to be raised. If an exception is raised, the first except clause that catches it will be evaluated.

You may be wondering why should we handle multiple exceptions. Depending on the exception raised, we can set up different behaviour for our application. For example, if a ZeroDivisionError, the issue is most likely in the user providing arguments, so we could inform them about it specifically. However, if a ValueError is thrown, it may be related to our logic. In that case we should be able to investigate the cause of it.

weight = 0
density = -40
volume = 5

try:
    weight = calculate_weight(density, volume)
except ZeroDivisionError:
    print("Division by zero exception was raised!")
except ValueError:
    print("Value error exception was raised!")
except TypeError:
    print("Type error exception was raised!")
Value error exception was raised!