
Python
Learn how to guarantee cleanup code runs, even when exceptions occur.
The `finally` block runs no matter whatβexception or not:
# Finally always runs
def example():
try:
print("In try block")
result = 10 / 0
except ZeroDivisionError:
print("In except block")
finally:
print("In finally block - ALWAYS runs!")
example()
# Output:
# In try block
# In except block
# In finally block - ALWAYS runs!
# Even if you return
def early_return():
try:
print("Trying...")
return "Success!"
finally:
print("Finally block runs before return!")
print(early_return())
# Output:
# Trying...
# Finally block runs before return!
# Success!Finally is critical for releasing resources:
# Without finally - file might not close if exception occurs
def bad_read_file(filename):
file = open(filename)
try:
content = file.read()
return content
except FileNotFoundError:
print("File not found!")
return None
# BUG: If exception occurs, file never closes!
# With finally - file ALWAYS closes
def good_read_file(filename):
file = open(filename)
try:
content = file.read()
return content
except FileNotFoundError:
print("File not found!")
return None
finally:
file.close()
print("File closed")
# Best: context manager (preferred)
def best_read_file(filename):
try:
with open(filename) as file:
content = file.read()
return content
except FileNotFoundError:
print("File not found!")
return None
# File automatically closes# Pattern 1: Database connections
def query_database():
connection = None
try:
connection = connect_to_db()
result = connection.execute("SELECT * FROM users")
return result
except ConnectionError:
print("Database connection failed")
return None
finally:
if connection:
connection.close()
print("Database connection closed")
# Pattern 2: File operations
def process_large_file(filename):
file = None
try:
file = open(filename)
lines = file.readlines()
return process_lines(lines)
except FileNotFoundError:
print("File not found")
return None
except ValueError:
print("Invalid data in file")
return None
finally:
if file:
file.close()
# Pattern 3: Lock/unlock resources
def critical_operation():
lock = acquire_lock()
try:
print("Performing critical operation...")
do_something()
except Exception as e:
print(f"Operation failed: {e}")
raise # Re-raise after logging
finally:
lock.release()
print("Lock released")Context managers automatically handle setup and cleanup:
# Using 'with' statement for automatic cleanup
def read_file(filename):
try:
with open(filename) as file:
return file.read()
except FileNotFoundError:
print(f"Cannot find {filename}")
return None
# File automatically closes here
# Process multiple files
def process_files(filenames):
for filename in filenames:
try:
with open(filename) as f:
content = f.read()
print(f"Processing {filename}...")
except FileNotFoundError:
print(f"Skipping {filename} - not found")
# File closes even if exception occurs
# Multiple context managers
def copy_file(source, destination):
try:
with open(source) as src, open(destination, 'w') as dst:
content = src.read()
dst.write(content)
print(f"Copied {source} to {destination}")
except FileNotFoundError:
print(f"Source file not found")
except IOError:
print(f"Cannot write to destination")
# Both files close automaticallyAll together in one block:
# Complete pattern
def process_data(filename):
file = None
try:
file = open(filename)
data = json.load(file)
except FileNotFoundError:
print(f"File {filename} not found")
except json.JSONDecodeError:
print(f"Invalid JSON in {filename}")
else:
# Only runs if no exception
print(f"Successfully loaded data from {filename}")
return data
finally:
# Always runs
if file:
file.close()
print("Cleanup complete")
# Better with context manager
def process_data(filename):
try:
with open(filename) as file:
data = json.load(file)
except FileNotFoundError:
print(f"File {filename} not found")
except json.JSONDecodeError:
print(f"Invalid JSON in {filename}")
else:
print(f"Successfully loaded data")
return data
# File closes automatically# Example 1: Database transaction
def transfer_money(from_account, to_account, amount):
connection = None
try:
connection = connect_to_database()
connection.begin_transaction()
# Debit from_account
connection.execute(
"UPDATE accounts SET balance = balance - ? WHERE id = ?",
(amount, from_account)
)
# Credit to_account
connection.execute(
"UPDATE accounts SET balance = balance + ? WHERE id = ?",
(amount, to_account)
)
connection.commit()
print(f"Transfer of ${amount} successful")
except InsufficientFundsError:
print("Insufficient funds")
if connection:
connection.rollback()
except DatabaseError as e:
print(f"Database error: {e}")
if connection:
connection.rollback()
finally:
if connection:
connection.close()
print("Database connection closed")
# Example 2: File cleanup with logging
def process_csv_file(filename):
input_file = None
output_file = None
try:
input_file = open(filename)
output_file = open(filename.replace(".csv", "_processed.csv"), "w")
reader = csv.reader(input_file)
writer = csv.writer(output_file)
for row in reader:
processed_row = [x.strip() for x in row]
writer.writerow(processed_row)
print(f"Successfully processed {filename}")
except FileNotFoundError:
print(f"File not found: {filename}")
except csv.Error as e:
print(f"CSV parsing error: {e}")
finally:
if input_file:
input_file.close()
if output_file:
output_file.close()
print("Files closed")
# Example 3: Temp file cleanup
import tempfile
import os
def create_report(data):
temp_file = None
try:
# Create temporary file
temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
temp_path = temp_file.name
# Write data
json.dump(data, temp_file)
temp_file.close()
# Process the file
process_report(temp_path)
except Exception as e:
print(f"Error creating report: {e}")
finally:
# Always delete temporary file
if temp_file:
try:
os.unlink(temp_path)
print(f"Temp file deleted: {temp_path}")
except OSError:
pass
# Example 4: Lock/resource tracking
class ResourceLock:
def __init__(self, name):
self.name = name
self.locked = False
def acquire(self):
self.locked = True
print(f"Lock acquired: {self.name}")
def release(self):
self.locked = False
print(f"Lock released: {self.name}")
def protected_operation():
lock = ResourceLock("database")
try:
lock.acquire()
print("Performing protected operation...")
# Could raise exception here
critical_code()
except Exception as e:
print(f"Operation failed: {e}")
finally:
lock.release()# If finally raises exception, it overwrites try/except exceptions
def problematic_finally():
try:
print("Trying...")
raise ValueError("Original error")
except ValueError as e:
print(f"Caught: {e}")
finally:
print("In finally...")
# This exception OVERWRITES the original!
raise RuntimeError("Finally error")
# Avoid this! Use safe cleanup:
def good_finally():
try:
print("Trying...")
raise ValueError("Original error")
except ValueError as e:
print(f"Caught: {e}")
finally:
print("In finally...")
try:
cleanup_code()
except:
print("Cleanup failed but original error takes priority")
# Better: don't raise in finally at all
def best_finally():
try:
print("Trying...")
raise ValueError("Original error")
except ValueError as e:
print(f"Caught: {e}")
finally:
try:
cleanup_code()
except:
pass # Log but don't raiseResources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep β’ Build Real β’ Verify Skills β’ Launch Forward