Introduction
The Python finally
clause is an option that can be added to try
statement when handling exceptions. This clause allows you to define a code block that must be executed whether an exception occurs or not. It is useful for performing cleanup operations, such as closing a file.
When a try
statement has a finally
clause, the except
clause, which handles the exceptions, becomes optional. In that case, exceptions will not be handled there, but the code that finally
provides will still execute.
Syntax of Python finally Clause
The finally
clause can be added to a try
statement, after the except
, clause to specify code that must always be executed, regardless of whether an exception occurs.
Here is its basic syntax:
try: try-block except ExceptionType: except-block finally: finally-block
try
: The keyword that starts the statement.try-block
: The block of code where you anticipate exceptions.except
: Defines the exception handling. When thefinally
clause is present, theexcept
clause is optional. There can also be more than oneexcept
clauses as in the usualtry
–except
syntax.except-block
: The code that handles the exception.finally
: Specifies a block of code to execute after thetry
block, regardless of whether an exception occurs.finally-block
: The code in this block is always executed, whether an exception is raised or not.
Using the finally Clause
The finally
clause is particularly useful for releasing external resources or performing cleanup operations, such as closing files, releasing network connections, or closing database connections. By placing cleanup code in the finally
block, you ensure that it gets executed even if an exception occurs within the try
block.
Let’s illustrate this with an example, which prompts the user to enter a numerator and denominator and then writes the resulting fraction to a text file:
Example:
try: file = open("example.txt", "w") numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator file.write(str(fraction)) print(f"{fraction} was written to file.") except (ZeroDivisionError, ValueError): print("Did not write to file because there was a problem with your input.") finally: file.close() print("File closed.")
Output:
Enter the numerator> 2 Enter the denominator> 5 0.4 was written to file. File closed.
Output:
Enter the numerator> 2 Enter the denominator> 0 Did not write to file because there was a problem with your input. File closed.
In the example above, the program opens a file for writing (line 2), prompts the user for input (lines 4 and 5), and calculates the resulting fraction by doing the division (line 6).
As you’ve learned from similar examples in the try
–except
lesson, this process has two potential problems that can raise exceptions. The user might enter 0 for the denominator, causing a ZeroDivisionError exception, or enter a value that can’t be converted to an integer, causing a ValueError exception.
If either of those does not happen, as in the first sample run (Output 1), the fraction is written to the file (line 8). If an exception does happen, as in the second output (Output 2), it is handled by the except
clause (lines 10 and 11).
In either case, we want to close the file after we are done. Therefore, the program has a finally
clause (line 12-14) that accomplishes that with a file.close()
statement (line 13).
Significance of Cleanup Operations
In programming, especially in scenarios involving file handling, database operations, or network connections, it’s important to manage resources efficiently. Failing to clean up, or release, resources properly can lead to memory leaks, resource exhaustion, or other undesirable consequences. This is where cleanup operations come into play.
Cleanup operations involve releasing resources, closing connections, or performing any necessary housekeeping tasks to ensure that the system remains in a consistent and efficient state. These operations are typically carried out after a certain task or operation is completed or when an error occurs in order to prevent resource over-consumption and maintain system stability.
The Python finally
clause plays a role in facilitating these cleanup operations. By providing a designated block of code that is guaranteed to execute regardless of whether an exception occurs, the finally
clause ensures that cleanup tasks are performed reliably.
Omitting the except Clause
Adding a finally
clause to the try
statement makes the except
clause optional. This will let all the exceptions go unhandled at this point. The code in the finally
block, however, will still be executed, and the exception raised following its completion.
Example:
try: file = open("example.txt", "w") numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator file.write(str(fraction)) print(f"{fraction} was written to file.") finally: file.close() print("File closed.")
Output 1:
Enter the numerator> 2 Enter the denominator> 5 0.4 was written to file. File closed.
Output 2:
Enter the numerator> 2 Enter the denominator> 0 File closed. Traceback (most recent call last): File "Contents/plugins/python-ce/helpers/pydev/pydevconsole.py", line 364, in runcode coro = func() File "<input>", line 6, in <module> ZeroDivisionError: division by zero
The example above is the same as the previous example, but with the except
clause deleted. When there is no exception, it runs the same way, as Output 1 shows. In the second run, there is a ZeroDivisionError.
In the second run (Output 2), the code in the finally
block executes, which closes the file and prints “File closed.” Following that, the exception is raised, but not handled by the code. It is ultimately handled by the terminal that ran it, which prints out the exception information.
In contrast, let’s see what happens when we don’t use the try
–finally
at all and let an exception happen.
file = open("example.txt", "w") numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator file.write(str(fraction)) print(f"{fraction} was written to file.") file.close() print("File closed.")
Output:
Enter the numerator> 2 Enter the denominator> 0 Traceback (most recent call last): File "Contents/plugins/python-ce/helpers/pydev/pydevconsole.py", line 364, in runcode coro = func() File "<input>", line 5, in <module> ZeroDivisionError: division by zero
This example just omits the try
and finally
lines. All the other code is the same. When we run it and input 0 for the denominator to let the ZeroDivisionError occur, the file is not closed and “File closed.” is not printed.
Unhandled Exceptions
If your try
statement does have an except
but it does not catch the exception that happened, the code behaves as if there is no except
, as described in the previous section. The finally
code is executed and then the exception raised.
Example:
try: file = open("example.txt", "w") numerator = int(input("Enter the numerator")) denominator = int(input("Enter the denominator")) fraction = numerator/denominator file.write(str(fraction)) print(f"{fraction} was written to file.") except ZeroDivisionError: print("Did not write to file because there was a problem with your input.") finally: file.close() print("File closed.")
Output:
Enter the numerator>? Hello File closed. Traceback (most recent call last): File "Contents/plugins/python-ce/helpers/pydev/pydevconsole.py", line 364, in runcode coro = func() File "<input>", line 4, in <module> ValueError: invalid literal for int() with base 10: 'Hello'
In the example above, the except
clause (line 10) now only handles ZeroDivisionError
. When we type “Hello” for the numerator, a ValueError
is generated because the input cannot be converted to an integer. Because ValueError
is not handled by the except
, the code in the finally
executes, and then the ValueError
exception is raised.
Best Practices for Using finally
When using the finally
clause, it is essential to adhere to best practices to ensure robust and maintainable code.
Here are some best practices to keep in mind:
- Keep the
finally
Block Simple: The code within thefinally
block should be kept as concise and straightforward as possible. Avoid complex logic or operations that could potentially raise other exceptions, as this could interfere with the intended cleanup process. - Avoid Nested
try
Statements: While it’s possible to nesttry
statements withfinally
clauses, doing so can make the code harder to read and maintain. Instead, consider refactoring the code to separate cleanup logic into separate functions or using context managers (with
statements) where appropriate. - Test Cleanup Code Thoroughly: Since cleanup operations are critical for maintaining system integrity, it’s essential to thoroughly test the cleanup code to ensure that it functions as expected in all scenarios, including normal execution and when exceptions occur.
Summary & Reference for the Python finally Clause
The finally
clause ensures that a block of code executes whether an exception occurs or not. It is useful for performing cleanup operations, such as closing a file.
The finally
clause can be optionally added to a try
statement and it augments its syntax.
try: try-block except ExceptionType: except-block finally: finally-block
When the finally
clause is present, the except
clause becomes optional, and if not present, allowing all exceptions to go unhandled at that point. However, the code in the finally
block will still execute, and the exception that occurred in the try
block will be raised following its completion.
If both an except
and finally
clauses are present, the except
must come before the finally
.
It’s essential to keep the code within the finally
block simple and free from operations that might raise exceptions as much as possible, ensuring that cleanup tasks are executed reliably.