Readings: exceptions
Contents
9.2. Readings: exceptions#
9.2.1. What are exceptions?#
In all the previous chapters we have seen a lot of errors. All these errors are called exceptions. Exceptions are raised when unexpected situations occur during program execution. Some of these situations include division by zero, input that does not match what the program is expecting, trying to use variables that were removed from the execution session, giving wrong input to functions, etc.
Exceptions, if not handled, result in termination of program execution since the computer would not know how to resolve the situation. To prevent the program from terminating unexpectedly, there are ways to handle or catch those exceptions, as we will see below. This is really useful when we want to write a program that will not terminate, but will instead perform a different action or produce a meaningful error message when an error is encountered.
9.2.2. Exception Handling#
During exception handling, we instruct the computer what to do in case an exception happens. The default behavior is for the computer to stop executing the program. But if we write code to handle the exception, then it will execute that code and will not terminate. The syntax to do this is as follows:
try:
code that we suspect might throw an exception
except Error:
code to be executed when error occurs
As you can see from above the code block has two parts: the try
and the except
part. The try
part specifies the block that will potentially raise an error, while in the except
part we instruct the computer what to do in case an error occurs.
Let us have a look at the examples below. The code does the same thing: it divides a number with a list of numbers to find the quotients.
numerator = 15
denominators = [0,3,5]
for denominator in denominators:
print(numerator/denominator)
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[2], line 2
1 for denominator in denominators:
----> 2 print(numerator/denominator)
ZeroDivisionError: division by zero
Since division by 0 does not make any sense, the program throws a ZeroDivisionError
and exits immediately.
try:
for denominator in denominators:
print(numerator/denominator)
except ZeroDivisionError:
print('Oops, you attempted to divide by zero! Program terminating...')
Oops, you attempted to divide by zero! Program terminating...
The error was raised again but this time the execution did not stop but went inside the except
clause and executed the statement in the print()
function. In this scenario it is usually said that the program finished executing gracefully.
Let’s see an example:
denominators_1 = [1, 2, 'hello']
try:
for denominator in denominators_1:
print(numerator/denominator)
except ZeroDivisionError:
print('Oops, you attempted to divide by zero! Program terminating...')
except TypeError:
print('Oops, an error has occurred!')
15.0
7.5
Oops, an error has occurred!
In this example, the error happened when the program tried to divide 15
by hello
. Since this is not a ZeroDivisionError
, the first except
clause is ignored and the second one TypeException
catches the error.
Note
In case you expect different types of errors to occur, then you can list them in except
clauses one after the other. Also, in case you are not sure what type of error is thrown, you can specify Exception
in the except
clause and it will catch (nearly) any type of error thrown. However, this is not usually a good practice in case you would like to distinguish between different types of errors.
Note
If in a list of except
clauses, you always put the Exception
first, then the other exceptions will always be ignored. Imagine Exception
as the set that contains most of the exceptions that can occur. Look at the example below. The code snippet in ZeroDivisionError
can never be reached.
try:
for denominator in denominators:
print(numerator/denominator)
except Exception:
print('Oops, an error has occurred!')
except ZeroDivisionError:
print('Oops, you attempted to divide by zero! Program terminating...')
Oops, an error has occurred!
Warning
It is never a good idea to catch an Exception
since it gives little information on what error occurred.
9.2.3. finally
Clause#
The finally
clause may be placed in a try
block after the except
statements. The code within the finally
clause always executes regardless of whether an exception executes or not. The syntax looks like this:
try:
code that we suspect might throw an exception
except ErrorType:
code that handles the exception
finally:
code to execute regardless of an exception happening
Usually, the finally
clause contains code that would release resources so that they can be used later, even if an exception occurs. An example would be:
try:
for denominator in denominators:
print(numerator/denominator)
except ZeroDivisionError:
print('Oops, you attempted to divide by zero!')
finally:
print('Program terminating...')
Oops, you attempted to divide by zero!
Program terminating...
In this example, the exception was raised, so the print statement in the except
block was executed and afterwards the control flow went in the finally
block that will execute the print()
statement there.
denominators_2 = [1,3,5]
try:
for denominator in denominators_2:
print(numerator/denominator)
except ZeroDivisionError:
print('Oops, you attempted to divide by zero!')
finally:
print('Program terminating...')
15.0
5.0
3.0
Program terminating...
In this case, no exception was raised, as a result the code block in the try
statement was executed successfully. After it was finished, the control went to the finally
block, printing the statement and terminating.
Note
You can create your own exceptions as well. In that case, to throw that exception from a try
block you can use the raise exception_name
statement and handle it in a corresponding except
statement. However, usually it is recommended that you use the already built-in exceptions in Python. You can find more information about them in the documentation.