Python finally Block
Table of Contents
In the last lesson, we learned about Python Multiple Exceptions. Now let’s use finally for code that must always run.
Let’s Talk About Cleanup
We know about try and except. We put risky code in try. We handle errors in except.
But sometimes, there is something we need to do no matter what. Whether an error happens or not. Whether the code succeeds or fails.
Maybe we opened a file. We need to close it. Maybe we started a database connection. We need to close it. Maybe we locked something. We need to unlock it.
That is what the finally block is for.
What Is finally in Python?
finally is a block of code that always runs. No matter what happens in the try block.
- If no exception happens,
finallyruns. - If an exception happens and is handled,
finallyruns. - If an exception happens and is not handled,
finallyruns before the program crashes.
It always runs. That is the whole point.
The Structure of Python try-except-finally
try: # code that might raise an exceptionexcept: # handle the exceptionfinally: # this always runsThe finally block is optional. But when we use it, it goes after all the except blocks.
We can also have try and finally without except.
try: # some codefinally: # cleanup codeIn this case, if an exception happens, it is not caught. It will still crash the program. But the finally block runs first. Then the exception propagates.
That is useful when we just want to make sure cleanup happens, even if we cannot handle the error.
A Simple finally Example in Python
Let’s see finally in action.
try: print("Trying something") print(10 / 0)except ZeroDivisionError: print("Caught division by zero")finally: print("This always runs")Output
Trying somethingCaught division by zeroThis always runsThe finally block ran. Even though there was an exception.
Now let’s see without an exception.
try: print("Trying something") print(10 / 2)except ZeroDivisionError: print("Caught division by zero")finally: print("This always runs")Output
Trying something5.0This always runsNo exception. But finally still ran.
That is the key idea. finally always runs.
finally with Unhandled Exception in Python
What if the exception is not handled? Let’s see.
try: print("Trying something") print(10 / 0)finally: print("This always runs")Output
Trying somethingThis always runsTraceback (most recent call last): File "example.py", line 3, in <module> print(10 / 0)ZeroDivisionError: division by zeroThe finally block ran before the program crashed. So even if we cannot handle the error, we can still clean up.
Why Is finally Useful in Python?
The most common use is closing resources.
When we open a file, we should close it when we are done. Even if an error happens while reading or writing.
file = open("data.txt", "r")try: content = file.read() print(content)finally: file.close()If an error happens during file.read(), the finally block still closes the file. That prevents file handles from leaking.
We can also use finally for other cleanup tasks. Like closing a network connection. Or releasing a lock. Or deleting temporary files.
finally with return in Python
Here is something interesting. If we have a return statement in the try or except block, the finally block still runs before the function returns.
def test(): try: return "From try" finally: print("Finally runs")
print(test())Output
Finally runsFrom trySee? The finally block ran before the return value was sent back. So even if we return early, cleanup still happens.
This is a very important behavior. It means we cannot skip the finally block.
finally with break or continue in Python
Similarly, if we have a loop with break or continue inside a try block, the finally block still runs.
for i in range(5): try: if i == 3: break print(i) finally: print("Finally for", i)Output
0Finally for 01Finally for 12Finally for 2Finally for 3When i is 3, break is executed. But before the loop exits, the finally block runs for that iteration.
That is the guarantee. finally always runs.
Python finally Block: A Real-Life Example
Let’s say we are writing to a file. We want to make sure the file is closed, even if something goes wrong.
try: file = open("output.txt", "w") try: file.write("Hello, world!\n") file.write("This is a test.\n") x = 10 / 0 finally: file.close() print("File closed")except ZeroDivisionError: print("Caught division by zero")Output
File closedCaught division by zeroThe inner finally closed the file. Then the outer except caught the division by zero.
That is a clean way to ensure cleanup.
A Common Pattern: try-finally for Cleanup
Sometimes we do not want to handle the exception. We just want to clean up and let the exception propagate.
def read_file(filename): file = open(filename, "r") try: return file.read() finally: file.close()If the file does not exist, open() raises FileNotFoundError. The finally block does not run because the file was never opened. But if the file opens successfully, the finally block runs and closes it.
If an error happens during read(), the finally block closes the file, and the exception propagates.
That is a robust pattern.
Common Mistakes with Python finally Block
Mistake 1: Putting Cleanup Code in the Wrong Place
try: file = open("data.txt", "r") content = file.read() file.close() # This might not run if an error happens before itexcept: print("Error")If file.read() raises an exception, file.close() is never called. The file stays open. That is a leak.
So what do we do? We put cleanup in finally.
try: file = open("data.txt", "r") content = file.read()except: print("Error")finally: file.close()But let’s be careful: if open() fails, file is not defined. So we need to handle that.
file = Nonetry: file = open("data.txt", "r") content = file.read()except: print("Error")finally: if file: file.close()Or better, we use a with statement. But that is a topic for later.
Mistake 2: Using finally to Return a Value
def test(): try: return "From try" finally: return "From finally"Output
From finallyThe finally block runs last. So its return value overrides the earlier one. That is confusing. Let’s avoid returning from finally.
Mistake 3: Putting Too Much Code in finally
Let’s keep the finally block simple. It should just do cleanup. Let’s not put logic that can fail. Because if finally raises an exception, it will mask the original exception.
Mistake 4: Forgetting That finally Runs Even on Unhandled Exceptions
If we have a try with finally but no except, and an exception happens, the finally runs, then the exception propagates. That is correct behavior. But some people forget it.
Next, we will learn about the Python raise Statement and create an exception when our code detects a problem.