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 thetry
clause block. This is useful in case we want some code to be executed after successful completion of thetry
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!