The Python finally Clause

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 the finally clause is present, the except clause is optional. There can also be more than one except clauses as in the usual tryexcept syntax.
  • except-block: The code that handles the exception.
  • finally: Specifies a block of code to execute after the try 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 tryexcept 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 tryfinally 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:

  1. Keep the finally Block Simple: The code within the finally 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.
  2. Avoid Nested try Statements: While it’s possible to nest try statements with finally 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.
  3. 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.