try / except / finally
The try, except, finally statements in Python handle exceptions—unexpected events that disrupt normal program flow. They let you catch errors, handle them gracefully, and ensure cleanup code always runs.
Syntax
try:
# Code that might raise an exception
risky_operation()
except SomeError:
# Handle the specific exception
handle_error()
except AnotherError as e:
# Access the exception instance
print(f"Error: {e}")
except (Error1, Error2):
# Handle multiple exception types
handle_multiple()
else:
# Runs if no exception was raised
success_path()
finally:
# Always runs, regardless of exception
cleanup()
Basic Exception Handling
Catch and handle specific exceptions:
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
# Output: Cannot divide by zero!
Access the exception object for more details:
try:
x = int("not a number")
except ValueError as e:
print(f"Conversion failed: {e}")
# Output: Conversion failed: invalid literal for int()...
Catching Multiple Exceptions
Handle different exceptions in different ways:
try:
data = {"key": "value"}
value = data["missing"]
num = int("abc")
except KeyError:
print("Key not found!")
except ValueError:
print("Invalid value!")
# Output: Key not found!
Catch multiple exceptions with a tuple:
try:
x = int("abc")
except (ValueError, TypeError) as e:
print(f"Error: {e}")
# Output: Error: invalid literal for int()...
The else Clause
The else block runs only when no exception occurs:
try:
x = int("42")
except ValueError:
print("Invalid number")
else:
print(f"Successfully converted: {x}")
# Output: Successfully converted: 42
This is useful for code that should run only on success:
try:
with open("config.txt") as f:
config = f.read()
except FileNotFoundError:
config = None
else:
print("Config loaded successfully")
# Output: Config loaded successfully (if file exists)
The finally Clause
The finally block always executes, regardless of whether an exception occurred:
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
content = None
finally:
# This always runs
print("Cleanup attempted")
# Output: Cleanup attempted
Use finally for cleanup that must happen:
try:
conn = connect_to_database()
result = conn.query("SELECT * FROM users")
except ConnectionError:
result = None
finally:
# Always close the connection
if conn:
conn.close()
Practical Examples
File handling with try/except/finally
def read_config(filename):
file = None
try:
file = open(filename, "r")
return file.read()
except FileNotFoundError:
return "{}" # Default config
finally:
if file:
file.close()
Better approach using context managers:
def read_config(filename):
try:
with open(filename, "r") as file:
return file.read()
except FileNotFoundError:
return "{}"
# No need for finally—the with statement handles cleanup
Safe numeric conversion
def safe_int(value, default=0):
try:
return int(value)
except (ValueError, TypeError):
return default
print(safe_int("42")) # Output: 42
print(safe_int("abc")) # Output: 0
print(safe_int(None)) # Output: 0
print(safe_int("99", 10)) # Output: 99
Retry pattern with finally
import time
def fetch_with_retry(url, max_retries=3):
for attempt in range(max_retries):
conn = None
try:
conn = connect(url)
return conn.fetch()
except ConnectionError as e:
print(f"Attempt {attempt + 1} failed: {e}")
if attempt == max_retries - 1:
raise
finally:
if conn:
conn.close()
time.sleep(1) # Wait before retry
Suppressing vs. handling exceptions
Sometimes you want to ignore certain errors:
# Suppress KeyError only
data = {"name": "Alice", "age": 30}
value = data.get("missing")
# Or explicitly suppress
try:
value = data["missing"]
except KeyError:
pass # Do nothing—just ignore the error
# Using dictionary get() is often clearer
value = data.get("missing", "default")
Exception Hierarchy
Python exceptions form a hierarchy. Catching a parent exception also catches its children:
try:
x = int("abc")
except Exception: # Catches all exceptions including ValueError
print("Something went wrong")
# More specific—catch only what you can handle
try:
risky_operation()
except ValueError:
# Handle value errors
pass
except Exception as e:
# Let other exceptions propagate
raise
Common exception types
- ZeroDivisionError — Dividing by zero
- ValueError — Wrong value type
- TypeError — Wrong operation on type
- KeyError — Missing dictionary key
- FileNotFoundError — File does not exist
- IndexError — List index out of range
- AttributeError — Missing attribute
- PermissionError — No permission
Raising Exceptions
Use raise to trigger exceptions (covered in detail in the raise keyword reference):
def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
return age
See Also
- class keyword — defining classes
- break keyword — exit loops
- continue keyword — skip loop iterations
- def keyword — defining functions that catch exceptions