Exception handling in Python provides a structured mechanism to intercept runtime errors, run corrective routines, and ensure resource cleanup without crashing the application. The complete statement consists of four distinct clauses: try, except, else, and finally.
1. The Anatomy of Exception Blocks
1try:
2# 1. Monitored Code: Operations that might trigger an exception
3 critical_operation()
4except SpecificError as err:
5# 2. Error Handling: Executes ONLY if SpecificError occurs in try block
6 handle_error(err)
7else:
8# 3. Clean Execution: Executes ONLY if NO exceptions occurred in try block
9 handle_success()
10finally:
11# 4. Mandatory Cleanup: ALWAYS executes, regardless of errors or returns
12 cleanup_resources()
2. Catching Multiple Exception Types
You can define multiple specialized except blocks or group related exceptions into a tuple.
Granular Individual Blocks (Recommended)
Handling different errors with specialized responses:
1defcompute_ratio(numbers, index1, index2):
2try:
3 val1 = numbers[index1]
4 val2 = numbers[index2]
5 result = val1 / val2
6except IndexError:
7print("Error: One or both indices are out of range.")
8returnNone
9except ZeroDivisionError:
10print("Error: Cannot divide by zero.")
11returnNone
12except TypeError:
13print("Error: List contains non-numeric values.")
Many developers mistakenly place too much code inside the try block. Doing so risks catching unintended exceptions from unrelated lines of code.
The else clause solves this by isolating code that should only run if the try block succeeded:
1# SUBOPTIMAL: If render_ui() raises KeyError, except block catches it incorrectly
2try:
3 data = load_database_record(user_id)
4 render_ui(data)
5except KeyError:
6print("User ID not found in database.")
7
8# PYTHONIC: Strict separation of error risk vs. post-success logic
9try:
10 data = load_database_record(user_id)
11except KeyError:
12print("User ID not found in database.")
13else:
14 render_ui(data)# Any error in render_ui() propagates normally
4. The Guarantee of finally
The finally suite executes before the try statement completes under all circumstances, including:
1
Normal completion of try and else.
2
Handled exceptions via an except block.
3
Unhandled exceptions that are propagating up the call stack.
4
Early exits via return, break, or continue.
1defread_first_line(filepath):
2 f =None
3try:
4 f =open(filepath,"r", encoding="utf-8")
5return f.readline().strip()
6except FileNotFoundError:
7return"DEFAULT_CONFIG"
8finally:
9if f:
10 f.close()
11print("File stream safely closed in finally.")
12
13result = read_first_line("system.ini")
Important Warning
Warning: Avoid return inside finally!
If a finally block executes an explicit return statement, any exception currently being raised is permanently discarded and silenced!
5. Execution Flow Summary Diagram
Scenario
try Runs?
except Runs?
else Runs?
finally Runs?
No errors occur
Yes
No
Yes
Yes
Caught error occurs
Stops at error
Yes
No
Yes
Uncaught error occurs
Stops at error
No
No
Yes (then halts)
return inside try
Yes
No
No
Yes (runs before return)
⚠️ Common Intermediate Mistakes & Gotchas
1. Putting Code That Can Raise Errors Inside the else Block
The else block runs only when NO exception was raised in the try block. Keep only operations that depend on success here, not code that requires its own exception handling.
2. Returning from finally
A return statement inside a finally block will silently suppress and discard any exception raised in try! Never return values inside finally.
💻 Try It Yourself: Resource Guardian Demonstration
Scenario
Demonstrate the full try-except-else-finally lifecycle by simulating a database transaction that ensures a connection is always closed in finally.
Complete Solution
1defexecute_transaction(amount):
2print("
3--- Starting Transaction ---")
4 connection_open =True
5try:
6print("Connected to database.")
7if amount <=0:
8raise ValueError("Transaction amount must be positive.")