Python’s exception handling syntax (try-except) has two lesser-known but very important optional clauses: else and finally. By combining these appropriately, you can clearly define “processing to execute only when no error occurs” and “cleanup processing to execute regardless of errors (like releasing resources).”
This article explains the complete syntax of try-except-else-finally and the role of each block.
Complete Syntax of Exception Handling
Exception handling consists of up to four blocks.
try:
# Process that might raise an exception
except ExceptionClass:
# Process when an exception occurs
else:
# Process when NO exception occurs
finally:
# Process that ALWAYS executes at the end, regardless of exceptions
else Clause: Processing on Success
The else block executes only if no exception occurred inside the try block. You might think, “Why not just write it at the end of the try block?” The benefit is keeping the try block minimal. By putting only “code that might raise an exception” in the try block and putting “safe processing using the result” in else, you reduce the risk of accidentally catching unintended exceptions with except.
finally Clause: Cleanup Processing
The finally block always executes at the end, whether an exception occurred or not. Even if a return statement is executed inside try or except, the finally block is passed through before the function exits. Therefore, it is ideal for “cleanup” processing like closing files, disconnecting database connections, or deleting temporary data.
Practical Code Example
As a concrete example, let’s create a function that simulates data loading from an external file.
- try: Attempt to load data (Potential error).
- except: Handle loading errors.
- else: Process and display loaded data (Execute if no error).
- finally: Disconnect connection (Always execute).
def process_file_data(filename):
print(f"--- Start processing '{filename}' ---")
# Mock data store
dummy_files = {
"data.txt": [10, 20, 30],
"empty.txt": []
}
try:
# 1. Load data (Risky process)
if filename not in dummy_files:
raise FileNotFoundError(f"File '{filename}' not found.")
data = dummy_files[filename]
# Calculation (Potential ZeroDivisionError if empty)
average = sum(data) / len(data)
except FileNotFoundError as e:
# 2. Error handling if file missing
print(f"[Error] File Error: {e}")
except ZeroDivisionError:
# 2. Error handling if data is empty
print("[Error] Calc Error: Cannot calculate average of empty data.")
else:
# 3. Processing on success
# Even if a new error occurs here, it won't be caught by the except blocks above (Safe)
print(f"[Success] Average calculated: {average:.2f}")
finally:
# 4. Cleanup (Always executes)
print("[Cleanup] File connection disconnected.")
print("--------------------------------\n")
# --- Execution Test ---
# Case 1: Success
process_file_data("data.txt")
# Case 2: File Not Found Error
process_file_data("unknown.csv")
# Case 3: Calculation Error
process_file_data("empty.txt")
Output:
--- Start processing 'data.txt' ---
[Success] Average calculated: 20.00
[Cleanup] File connection disconnected.
--------------------------------
--- Start processing 'unknown.csv' ---
[Error] File Error: File 'unknown.csv' not found.
[Cleanup] File connection disconnected.
--------------------------------
--- Start processing 'empty.txt' ---
[Error] Calc Error: Cannot calculate average of empty data.
[Cleanup] File connection disconnected.
--------------------------------
Key Behavior Points
- Success (data.txt): Executed in order
try->else->finally. - Failure (unknown.csv, empty.txt): Executed in order
try->except->finally. - Regardless of the outcome, the
[Cleanup]message in thefinallyblock was always output.
Summary
else: Executed when no exception occurs. Use it to narrow down thetryblock to only code that needs exception monitoring.finally: Always executed at the end, regardless of exceptions. Use it to guarantee cleanup processing like releasing resources.
By mastering these, you can write robust programs that are resistant to errors and free from resource leaks.
