Exception types and how to catch them
The finally block executes unconditionally after try and except blocks, making it ideal for resource cleanup.
When Cleanup Gets Skipped
Opening a file asks the operating system to allocate a resource called a file handle. The file begins in an open state. If an exception interrupts the program before the file is closed, the file can remain open. This resource leak wastes system memory and can prevent other programs from accessing the file.
The difficulty is that an exception can occur at different points in resource-dependent code. Cleanup written after the risky operations may never be reached. The finally block solves this control-flow problem by running after the try and except blocks, including when an exception interrupts the try block.
The finally Guarantee
A try block contains operations that may raise an exception. An except block handles a specified exception path. The finally block contains cleanup that must be attempted after those paths. Its defining property is that it executes unconditionally after the try and except blocks in normal exception-handling situations.
finally runs whether the try block succeeds, an exception is caught by an except block, or an exception is not caught. That makes it the appropriate place to close files, release locks, or free other resources.
Matching Exception Handlers
An except block can be associated with a particular exception type. The source example handles IOError when the file is not found and KeyboardInterrupt when the user presses Ctrl+C. These are different interruption paths, but both paths still reach finally afterward.
This example follows the resource-cleanup pattern described in the source. The variable f is assigned None before the try block. If opening the file succeeds, f refers to the opened file and finally can close it. If open fails with IOError, f remains None, so the cleanup check does not try to close a resource that was never acquired.
Tracing the File State
A File That Opens Before an Error
Trace the resource state when the file opens successfully, a later operation raises an exception, and finally performs cleanup.
Before try: f is initialized to None. No file object has yet been assigned to it.
During try: open() succeeds, so f refers to the file and the file enters the open state.
Exception: A later operation raises an exception. Execution leaves the interrupted operation and proceeds through the applicable exception-handling path.
finally: The existence check confirms that f refers to a resource, so close() is called.
After cleanup: The file transitions from open to closed, preventing the resource from being left open by this interrupted path.
The file is closed even though the exception interrupted the resource-dependent work.
What do you think happens?
Suppose open() fails immediately. What value does f have when finally runs in the example?
Reveal answer
Answer: It is None.
f was initialized to None before try. If open() fails, no file object is assigned, and the existence check prevents finally from calling close() on an unassigned resource.
Safe Cleanup Rules
- Initialize each resource variable to None before entering try.
- Place resource acquisition inside try so an acquisition failure can be handled.
- Check that the resource exists before calling its cleanup operation in finally.
- Put cleanup in finally rather than relying on statements that follow the risky operations.
- Use separate except handlers when the source distinguishes different exception types, such as IOError and KeyboardInterrupt.
finally runs in almost all normal circumstances. Rare exceptions include forceful termination, such as a system kill signal or os.exit(). Also, if finally raises an exception of its own, that new exception propagates after finally completes. Therefore, finally is the practical guarantee for normal exception-handling scenarios, not an absolute guarantee against forced termination.
Mistakes Beginners Make
Closing the file only after the try block
Execution may never reach that later cleanup statement, leaving the file open.
Fix:
Put the close operation in finally so it is reached after the success path and exception-handling path.Calling close() without checking whether the file opened
The file variable may still be None, or it may never have been safely assigned.
Fix:
Initialize the variable to None before try and check whether it refers to a resource before closing it.Assuming finally runs only when an exception is caught
finally also runs when the try block succeeds.
Fix:
Think of finally as the cleanup path for every normal outcome, not just the caught-exception outcome.Treating every exception as the same path
The source example distinguishes IOError from KeyboardInterrupt and gives each its own except handler.
Fix:
Match the exception type to its appropriate handler, then remember that both paths proceed to finally.
Practice the Trace
Trace both outcomes of the file-cleanup pattern. First, assume open() succeeds and all file operations finish normally. Second, assume open() succeeds but a later operation raises an exception. For each outcome, identify whether the file reaches the closed state and explain which part of the structure performs that cleanup.
Hints
- Start with f = None.
- Ask whether open() assigns a file object to f.
- Then follow the path to finally.
- The cleanup check should call close() only when the resource exists.
Explain what happens if open() raises IOError before assigning a file object. Your explanation should include the value prepared before try, the matching exception handler, and why the cleanup check does not attempt to close a nonexistent file object.
Hints
- The variable is initialized before try.
- IOError is the source example's file-related exception path.
- finally still runs after the handler.
- The existence check determines whether close() is called.
Reliable Resource Handling
- The finally block is the cleanup guarantee in a try, except, finally structure. It runs after successful execution and after exception-handling paths, so it is the right place to close files and release other resources. Initialize resource variables before try, check that a resource exists before cleaning it up, and remember that code placed after interrupted operations may never run. Specific except handlers, such as those for IOError and KeyboardInterrupt, can handle different exception paths while still converging on the same finally cleanup.
Key Takeaways
- finally executes after try and except paths in normal exception-handling situations.
- Resource cleanup belongs in finally because an exception can bypass later statements.
- Initialize resource variables to None before try and check that they exist before closing them.
- Different exception types can use different except handlers before reaching the same cleanup path.
- A file transitions from open to closed in finally even when an exception interrupts file-dependent work.