- Use try/except blocks to handle errors
- Catch specific exception types
- Use else and finally clauses
- Handle multiple exceptions gracefully
Try and Except
Imagine you're walking on a tightrope. Without a safety net, one mistake and it's over! But with a net below, you can take risks, and if you fall, you land safely. Try/except is your program's safety net – it lets you "try" risky code, and if something goes wrong, you're "caught" by the except block instead of crashing.
Exception handling transforms fragile programs that crash at the first sign of trouble into robust applications that handle problems gracefully.
Basic Try/Except
# Without try/except - program crashes!
number = int("hello") # ValueError crashes the program
# With try/except - program continues!
try:
number = int("hello")
except:
print("That wasn't a valid number!")
number = 0
print(f"Continuing with number = {number}")
The Flow
Try/Except Flow
try:
risky_code()
NO ERROR ERROR OCCURS
Skip except block except:
handle_error()
Continue program
Catching Specific Exceptions
Don't catch everything blindly – be specific!
# BAD: Catching everything hides real problems
try:
result = do_something()
except:
pass # Silently ignores ALL errors (even bugs!)
# GOOD: Catch specific exceptions
try:
number = int(user_input)
except ValueError:
print("Please enter a valid number")
Why Be Specific?
# This hides a bug in your code!
try:
user_nme = "Alice" # Typo: should be user_name
print(user_name)
except:
print("Something went wrong") # You'll never find the typo!
# This catches only what you expect
try:
number = int(user_input)
except ValueError:
print("Invalid number")
# NameError from typo will still be raised (good!)
Catching Multiple Exceptions
Method 1: Multiple Except Blocks
try:
value = my_dict[key]
result = 10 / value
except KeyError:
print("Key not found in dictionary")
except ZeroDivisionError:
print("Can't divide by zero")
except TypeError:
print("Invalid type for division")
Method 2: Tuple of Exceptions
try:
result = 10 / int(user_input)
except (ValueError, ZeroDivisionError):
print("Invalid input or division by zero")
Method 3: Access the Exception
try:
result = 10 / int(user_input)
except ValueError as e:
print(f"Value error: {e}")
except ZeroDivisionError as e:
print(f"Math error: {e}")
The else Clause
The else block runs only if no exception occurred:
try:
number = int(user_input)
except ValueError:
print("Invalid number!")
else:
# Only runs if try succeeded
print(f"Great! Your number is {number}")
result = number * 2
Why Use else?
Why Use else?
WITHOUT else:
try:
number = int(user_input)
# This code runs in try, but exceptions here
# would be caught too!
result = complex_calculation(number)
except ValueError:
print("Invalid") # Catches errors from BOTH lines!
WITH else:
try:
number = int(user_input) # Only this is protected
except ValueError:
print("Invalid number")
else:
# Errors here are NOT caught (bugs should be visible!)
result = complex_calculation(number)
The finally Clause
The finally block ALWAYS runs, no matter what:
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found!")
finally:
# This ALWAYS runs
print("Cleanup complete")
Classic Use: Resource Cleanup
file = None
try:
file = open("data.txt", "r")
data = file.read()
# Process data...
except FileNotFoundError:
print("File not found")
finally:
if file:
file.close() # Always close, even if error occurred!
print("File operation complete")
Better: Use 'with' Instead
# The 'with' statement handles cleanup automatically
try:
with open("data.txt", "r") as file:
data = file.read()
except FileNotFoundError:
print("File not found")
# No finally needed - 'with' closes the file!
Complete Structure
try:
# Code that might raise an exception
risky_operation()
except SpecificError as e:
# Handle specific error
handle_error(e)
except AnotherError:
# Handle another error type
handle_another()
else:
# Runs only if try succeeded (no exceptions)
success_code()
finally:
# ALWAYS runs (cleanup)
cleanup()
Practical Examples
Example 1: Safe Number Input
def get_number(prompt):
"""Safely get a number from user input."""
while True:
try:
return float(input(prompt))
except ValueError:
print("That's not a valid number. Try again.")
# Usage
age = get_number("Enter your age: ")
print(f"You are {age} years old")
Example 2: Safe Dictionary Access
def get_user_info(users, user_id):
"""Safely get user information."""
try:
user = users[user_id]
name = user["name"]
email = user["email"]
except KeyError as e:
print(f"Missing data: {e}")
return None
else:
return {"name": name, "email": email}
users = {
1: {"name": "Alice", "email": "alice@example.com"},
2: {"name": "Bob"} # Missing email
}
print(get_user_info(users, 1)) # Works
print(get_user_info(users, 2)) # Missing email
print(get_user_info(users, 3)) # User doesn't exist
Example 3: Safe File Reading
def read_config(filename):
"""Safely read a configuration file."""
try:
with open(filename, "r") as file:
content = file.read()
except FileNotFoundError:
print(f"Config file '{filename}' not found. Using defaults.")
return {}
except PermissionError:
print(f"No permission to read '{filename}'")
return {}
else:
# Parse the content only if file was read successfully
config = {}
for line in content.strip().split("\n"):
if "=" in line:
key, value = line.split("=", 1)
config[key.strip()] = value.strip()
return config
config = read_config("settings.txt")
Common Mistakes
1. Catching Too Broadly
# BAD: Hides all errors, including bugs
try:
result = calculate(x)
except:
pass
# GOOD: Catch specific exceptions
try:
result = calculate(x)
except ValueError:
result = default_value
2. Empty Except Blocks
# BAD: Silently ignores errors
try:
data = load_data()
except Exception:
pass # What happened? No one knows!
# GOOD: At least log the error
try:
data = load_data()
except Exception as e:
print(f"Warning: Could not load data: {e}")
data = []
Key Takeaways
Remember These Points
try: Contains code that might fail
except: Handles the error
Be SPECIFIC about which exceptions to catch
except ValueError: instead of bare except:
else: Runs only if try succeeded
Good for code that should only run on success
finally: ALWAYS runs (even after return!)
Use for cleanup (closing files, connections)
Access exception: except ValueError as e:
What's Next?
You can now catch errors! But what if YOU want to signal that something went wrong? In the next lesson, we'll learn raising exceptions – how to create and throw your own errors.
