Python language basics 62: clean-up after exceptions with finally
November 8, 2015 Leave a comment
Introduction
In the previous post we discussed how to re-raise an exception using the raise keyword. You can use “raise” in order to re-throw the most recent exception with all its details so that it’s not silently suppressed or replaced with another exception which lacks the necessary details in order to locate bugs.
In this post we’ll see how to run a code block irrespective of whether an exception was raised or not.
The finally keyword
The finally keyword can be used in conjunction with try and except. The code block in finally is always executed even if an exception is thrown. Very often the finally block is used in order to restore some state that might have been altered in the try block.
Probably the most common example is file handling. Opening and reading from or saving to a file is considered as error-prone operations: the file might not exist, the caller may not have the necessary access, the file may be corrupted etc. So at some point your code in the try-block will throw an exception leaving the file still open, unsaved or some other undesirable state. The purpose of the finally-block is that the code within it will always run no matter whether the code in the try block threw an exception. Therefore you can always be sure that any code which restores some state will be executed.
The finally block can be used with or without an except-block. Consider the following version of the code from the previous post:
def divide(first, second): res = 0 try: res = first / second except ZeroDivisionError: print("Big NONO") raise return res try: res = divide(10, 2) print(res) print("I am here.") finally: print("This statement is always executed.") print("...and this one as well.")
Note how the try-block is followed by the finally-block and the except-block is omitted. Running the above code will produce the following output:
5.0
I am here.
This statement is always executed.
…and this one as well.
There was obviously no exception thrown and the finally block was executed normally.
If we call the divide function as follows…:
try: res = divide(10, 0) print(res) print("I am here.") finally: print("This statement is always executed.") print("...and this one as well.")
…then we get the following output:
Traceback (most recent call last):
Big NONO
File “C:/PythonProjects/HelloWorld/Various.py”, line 11, in
This statement is always executed.
res = divide(10, 0)
…and this one as well.
File “C:/PythonProjects/HelloWorld/Various.py”, line 4, in divide
res = first / second
ZeroDivisionError: division by zero
Note that the exception raised by the divide function was not caught but the finally-block was still executed.
Here’s how you can pair up the except and finally blocks:
try: res = divide(10, 0) print(res) print("I am here.") except Exception as err: print(type(err)) print(err.args) print(err) finally: print("This statement is always executed.") print("...and this one as well.")
This will produce the following output:
Big NONO
(‘division by zero’,)
division by zero
This statement is always executed.
…and this one as well.
Read the next post here.
Read all Python-related posts on this blog here.