Finally & Else
The finally clause guarantees code runs no matter what - whether an
exception occurs, the code returns early, or breaks from a loop. The else clause
runs only when no exception occurred. Together, they complete Python's exception handling toolkit,
enabling robust resource management and clean code separation!
Finally Block Basics
The finally block always executes - when an exception occurs, when no exception
occurs, even when the code uses return, break, or continue.
This makes it perfect for cleanup code that must run no matter what happens.
Click Run to execute your code
- After
try completes normally- After
except handles an exception- Before
return completes- Before
break or continue in loops- Before an exception propagates up
Key insight: Finally runs BEFORE control leaves the try/except block!
Finally with Return Statements
When a function returns inside a try block, finally still runs - but there's a catch. If finally also has a return statement, it overrides the try's return! This is usually a bug, so avoid return statements in finally blocks.
Click Run to execute your code
-
return - Overrides the try block's return value-
raise - Replaces the original exception-
break/continue - Overrides loop controlFinally should ONLY contain cleanup code - closing files, releasing locks, etc.
The Else Clause
The else clause runs only when no exception occurred in the try block. This
lets you separate "code that might fail" from "code to run on success". It keeps your try
block minimal and makes the code's intent clearer.
Click Run to execute your code
1.
try - Runs first2.
except - Only if exception occurred3.
else - Only if NO exception occurred4.
finally - Always runs lastWhy use else? Keep try blocks minimal - only wrap code that might fail!
Resource Management Patterns
The most common use of finally is resource cleanup - closing files, disconnecting from
databases, releasing locks. But Python's with statement (context managers)
is usually cleaner. Still, understanding the finally pattern helps when with isn't available.
Click Run to execute your code
with open(file) as f: - File automatically closedwith lock: - Lock automatically releasedwith connection: - Connection automatically closedUse try/finally only when context managers aren't available or appropriate.
Common Mistakes
1. Returning in finally
# Wrong - return in finally overrides try's return!
def get_data():
try:
return fetch_from_server()
finally:
return None # This ALWAYS returns None!
# Correct - only cleanup in finally
def get_data():
connection = None
try:
connection = connect()
return connection.fetch()
finally:
if connection:
connection.close() # Just cleanup, no return
2. Raising in finally (suppresses original)
# Wrong - new exception replaces original!
def process():
try:
raise ValueError("Original error")
finally:
raise RuntimeError("Cleanup failed") # Original lost!
# Correct - log cleanup errors, don't raise
def process():
try:
raise ValueError("Original error")
finally:
try:
cleanup()
except Exception as e:
logging.error(f"Cleanup failed: {e}")
# Don't raise - let original propagate
3. Confusing else with finally
# Wrong - using else for cleanup
try:
data = load_data()
except FileNotFoundError:
data = default_data()
else:
file.close() # Wrong place! What if exception occurred?
# Correct - else for success-only code, finally for cleanup
try:
data = load_data()
except FileNotFoundError:
data = default_data()
else:
process(data) # Only runs on success
finally:
file.close() # Always runs
4. Too much code in try block
# Wrong - everything in try
try:
f = open(filename)
data = f.read()
parsed = json.loads(data)
result = process(parsed)
save(result)
except Exception:
print("Something failed") # Which part?
# Correct - minimal try, use else for success path
try:
f = open(filename)
except FileNotFoundError:
print("File not found")
f = None
else:
try:
data = f.read()
parsed = json.loads(data)
except json.JSONDecodeError:
print("Invalid JSON")
else:
result = process(parsed)
save(result)
finally:
if f:
f.close()
5. Not using context managers when available
# Verbose - manual try/finally
f = None
try:
f = open(filename)
data = f.read()
finally:
if f:
f.close()
# Better - context manager handles cleanup
with open(filename) as f:
data = f.read()
# Automatically closed, even if exception occurs!
# For multiple resources
with open('in.txt') as infile, open('out.txt', 'w') as outfile:
outfile.write(infile.read())
Exercise: Safe Database Query
Task: Create a function that safely queries a database with proper cleanup.
Requirements:
- Connect to the database in try block
- Use else for processing the results
- Always disconnect in finally, even if errors occur
- Return the results or an error message
Click Run to execute your code
Show Solution
class Database:
"""Simulated database for exercise."""
def __init__(self, name):
self.name = name
self.connected = False
def connect(self):
self.connected = True
print(f"Connected to {self.name}")
def disconnect(self):
if self.connected:
self.connected = False
print(f"Disconnected from {self.name}")
def query(self, sql):
if not self.connected:
raise RuntimeError("Not connected!")
if "error" in sql.lower():
raise ValueError("Query syntax error")
return [{"id": 1}, {"id": 2}]
def safe_query(db, sql):
"""
Safely query database with proper cleanup.
Returns (success, result_or_error).
"""
try:
db.connect()
except Exception as e:
return (False, f"Connection failed: {e}")
else:
# Only runs if connect succeeded
try:
results = db.query(sql)
return (True, results)
except ValueError as e:
return (False, f"Query failed: {e}")
finally:
# Always disconnect
db.disconnect()
# Test the function
db = Database("production")
print("=== Test 1: Successful query ===")
success, result = safe_query(db, "SELECT * FROM users")
print(f"Success: {success}, Result: {result}\n")
print("=== Test 2: Failed query ===")
success, result = safe_query(db, "SELECT error FROM bad")
print(f"Success: {success}, Result: {result}\n")
print("=== Verify cleanup ===")
print(f"Database connected: {db.connected}") # Should be False
Summary
- finally: Always runs - perfect for cleanup code
- else: Runs only when no exception occurred
- Order: try → except (if error) OR else (if no error) → finally
- finally with return: Runs before return, but avoid returning in finally
- Don't in finally: return, raise, break, continue
- Use else to: Keep try blocks minimal, separate success code
- Resource management: Finally ensures cleanup happens
- Prefer context managers:
withis cleaner than try/finally
What's Next?
Now you know how to catch and clean up after exceptions. But what if you need to
signal an error yourself? Next, we'll learn about raise to create
and throw your own exceptions, and how to create custom exception classes for your
specific error conditions!
Enjoying these tutorials?