Finally Blocks
A finally block is code that must run no matter what—whether the try block succeeds, whether an exception is caught by an except block, or even if an exception is not caught at all.
Try it: try / except / else / finally
How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.
How it works
- Statements in try run until one raises an exception; the rest of try is skipped.
- Python checks the except clauses in order and runs the first one whose type matches.
- If nothing was raised, the else block runs instead.
- finally runs in every case.
Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.
Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.
Loading the simulation…
Try it: Context Managers and the with Statement
What with really does: it calls __enter__ before the block and __exit__ after it — even when an exception leaves the block — so a file opened with with is always closed before the exception continues; try/finally gives the same guarantee by hand, a bare close() is skipped by the exception, and a context manager whose __exit__ returns True suppresses the exception.
How it works
- with EXPR as f: evaluates EXPR (e.g. open(...)), calls its __enter__() and binds the returned value to f (a file's __enter__ returns the file itself).
- The block runs. If a statement raises (here int() on text that is not a number), the rest of the block is skipped.
- Whether the block finished or an exception is leaving it, Python calls __exit__(exc_type, exc, tb) — for a file this closes it. This happens before any outer except sees the exception.
- If __exit__ returns a true value the exception is suppressed and execution continues after the block; a file's __exit__ returns None, so the exception propagates to the nearest matching except (or stops the program).
- try: … finally: f.close() gives the same cleanup by hand (start with f = None and check it); open() … f.close() without either leaves the file open when an exception jumps past close().
Default run (10 steps): The program opens data.txt, which contains '4x\n'. Watch when the file is closed — and what happens if int() raises ValueError. … The program finished. The file was closed.
Simplified: Fixed program templates reading one line of data.txt (text you type, up to 12 ASCII characters) and converting it with int(). A small interpreter written for these statements runs them — nothing is executed — and it reproduces CPython 3.12's sys.settrace line/call/exception events, printed output, final error line and file state for every combination. File objects' own __enter__/__exit__ are C code, so they appear as protocol steps rather than traced lines.
Loading the simulation…
The Resource Cleanup Problem
Imagine you open a file in your program to read data. Your code processes the file successfully, and you close it. But what if an exception is raised while you are reading? If you do not handle that exception carefully, your program might crash before reaching the line that closes the file. The file object remains open, consuming system resources. This is a resource leak. The finally block solves this problem by guaranteeing that certain cleanup code runs whether or not an exception was raised.
A finally block is code that must run no matter what—whether the try block succeeds, whether an exception is caught by an except block, or even if an exception is not caught at all.
How Finally Blocks Execute
The finally block is an optional part of a try-except statement. It comes after all except blocks (if any exist). The key guarantee is this: the finally block always executes, regardless of the path taken through the try-except structure. If the try block completes without raising an exception, the finally block runs. If an exception is raised and caught by an except block, the except block runs first, then the finally block runs. If an exception is raised and not caught, the finally block still runs before the exception propagates up to the caller.
Structure and Syntax
A try-except-finally statement has a specific structure. The try block is required. At least one except block or a finally block (or both) must follow the try block. The finally block, if present, always comes last, after all except blocks. You cannot have a finally block without a try block.
File Resource Lifecycle with Finally
Consider what happens when you open a file. The file object is created and holds a connection to the file on disk. If you read from the file and an exception occurs, the file remains open unless you explicitly close it. The finally block is the ideal place to close the file because it guarantees the close operation runs regardless of whether an exception occurred.
Worked Example: Reading a File Safely
Using Finally to Guarantee File Closure
Write code that opens a file, reads its contents, and ensures the file is closed even if an error occurs while reading.
- 1Open the file in the try block
The try block is where you acquire the resource. You open the file here because you want to attempt the operation that might fail.
- 2Read and process the file contents
Inside the try block, perform the operations that might raise an exception, such as reading lines or parsing data.
- 3Add an except block to handle specific errors
If an exception occurs during reading, the except block catches it and handles it gracefully. For example, you might print an error message or use a default value.
- 4Add a finally block to close the file
The finally block runs after the try and except blocks, regardless of whether an exception occurred. This is where you close the file, ensuring the resource is always released.
The file is guaranteed to be closed whether the read succeeds or fails, preventing resource leaks.
File not found
(or the file contents if successful)
File is closed in both casesWhy Finally Guarantees Cleanup
Without a finally block, you might write cleanup code at the end of your try-except block. However, if an exception is raised and not caught, the program jumps to the exception handler and skips any code after the exception point. The finally block solves this by being part of the exception handling mechanism itself. Python guarantees that the finally block runs before the exception propagates to the caller, ensuring cleanup happens even when an exception is not caught.
Common Mistakes
Common mistake Putting resource cleanup code inside the try block after the main operation
What it looks liketry: file = open('data.txt') content = file.read() file.close() # If read() raises an exception, this line is skippedWhy it failsIf an exception is raised during the main operation, the cleanup code is never reached. The file remains open.
Do this insteadMove the cleanup code to a finally block so it runs regardless of exceptions.
Common mistake Forgetting to handle the case where the resource was never successfully acquired
What it looks liketry: file = open('data.txt') except FileNotFoundError: pass finally: file.close() # file may not exist if open() failedWhy it failsIf
open()raises an exception, the file variable is never created. Callingfile.close()in finally will raise aNameError.Do this insteadInitialize the variable before the try block or check if it exists in finally: if 'file' in locals(): file.close()
Common mistake Assuming finally prevents the exception from propagating
What it looks liketry: raise ValueError('Something went wrong') finally: print('Cleanup done') # Program crashes here with ValueErrorWhy it failsThe finally block runs, but it does not catch or suppress the exception. After finally completes, the exception continues to propagate.
Do this insteadUse an except block to catch and handle the exception if you want to prevent it from propagating.
Common mistake Using finally for logic that should be in except
What it looks liketry: result = 10 / 0 finally: print('Division failed')Why it failsThe finally block runs regardless of whether an exception occurred. If you want to run code only when an exception occurs, use except.
Do this insteadUse except ZeroDivisionError: print('Division failed') to handle the specific error.
The Modern Alternative: Context Managers
While finally blocks are a fundamental tool for resource cleanup, Python provides a more elegant approach called context managers, implemented using the with statement. When you use with to open a file, Python automatically calls a cleanup method (called __exit__) when the block exits, whether or not an exception occurred. This eliminates the need to write explicit try-finally blocks for common resource management tasks.
Both approaches ensure the file is closed. The with statement is cleaner and preferred for file operations.The with statement (context manager) is the modern, preferred way to handle resource cleanup in Python. However, understanding finally blocks is essential because they are the underlying mechanism that makes context managers work, and they are still used in situations where a with statement is not available.
Practice: Applying Finally Blocks
Write a try-except-finally block that attempts to read a configuration file. The try block should open and read the file. The except block should handle FileNotFoundError by printing a message. The finally block should close the file. What happens if the file does not exist? Does the finally block still run?
Hints
- Remember that if
open()fails, the file variable is not created. Consider initializing it before the try block. - The finally block always runs, even if the file was never successfully opened.
- You can check if the file is open before closing it using
hasattr(file, 'close').
Summary
Finally blocks are a critical tool for ensuring resource cleanup in Python. They guarantee that cleanup code runs regardless of whether an exception occurs, preventing resource leaks. A finally block always executes after the try and except blocks, even if an exception is raised and not caught. While finally blocks are essential for understanding exception handling, the modern with statement (context manager) is the preferred way to handle resource cleanup for most common scenarios. Understanding both approaches will make you a more effective Python programmer.
At a glance
- A finally block is code that always runs after a try-except block, whether an exception occurs or not.
- Finally blocks guarantee resource cleanup by running before exceptions propagate to the caller.
- The finally block comes after all except blocks and is optional, but at least one except or finally block must follow the try block.
- Common use cases include closing files, releasing database connections, and other resource management tasks.
- Modern Python code often uses the with statement (context managers) instead of explicit try-finally blocks for cleaner, more readable code.