⚙️
Intermediate
try · except · else · finally · raise · Custom exceptions
Week 5 — Exception Handling
Write robust programs by handling errors gracefully. Learn the full try/except/else/finally pattern, re-raise exceptions, and define custom exception classes.
exceptiontryexceptraisecustom exception
Duration
⏱ 2 hours
Level
📊 Intermediate
Prerequisite
🎯 Basic Week 9
OUTCOME
Wrap a file-reading function with proper error handling and a custom exception
What you'll learn
- 1Catch specific exceptions with except ExceptionType
- 2Use else (runs when no exception) and finally (always runs)
- 3Re-raise exceptions with raise
- 4Create custom exception classes
- 5Log exceptions and provide user-friendly error messages
1. try / except / else / finally
python
def read_int(prompt):
try:
return int(input(prompt))
except ValueError as e:
print(f"Invalid input: {e}")
return None
try:
with open("data.txt") as f:
data = f.read()
except FileNotFoundError:
print("File not found")
except PermissionError:
print("Permission denied")
else:
print("File read successfully")
finally:
print("Done (always runs)")2. Custom Exceptions
python
class ValidationError(ValueError):
"""Raised when user input fails validation."""
def __init__(self, field, message):
super().__init__(f"{field}: {message}")
self.field = field
def validate_age(age):
if not isinstance(age, int):
raise ValidationError("age", "must be an integer")
if age < 0 or age > 150:
raise ValidationError("age", "must be between 0 and 150")
try:
validate_age(-5)
except ValidationError as e:
print(f"Validation failed — {e}")3. Exception Hierarchy
ℹ️
BaseException → Exception → ValueError, TypeError, OSError (FileNotFoundError, PermissionError), IndexError, KeyError, etc. Catch the most specific exception you can handle.
4. Common Mistakes
- Bare except: (no type) catches everything including KeyboardInterrupt and SystemExit. Always specify a type.
- Swallowing exceptions silently: except Exception: pass hides bugs. At minimum log them.
- Using exceptions for normal control flow — e.g., catching ValueError to detect end of input instead of checking first.
💻 Examples
Run these examples and check the output yourself.
01_safe_divide.py— Division with multiple exception handling
CODE
def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
return None, "Cannot divide by zero"
except TypeError as e:
return None, f"Type error: {e}"
else:
return result, None
for a, b in [(10, 2), (10, 0), (10, "x")]:
val, err = safe_divide(a, b)
if err:
print(f"{a}/{b} → Error: {err}")
else:
print(f"{a}/{b} = {val}")
▶ Output
10/2 = 5.0
10/0 → Error: Cannot divide by zero
10/x → Error: Type error: unsupported operand type(s) for /: 'int' and 'str'02_custom_exc.py— Custom exception hierarchy
CODE
class AppError(Exception): pass
class DatabaseError(AppError): pass
class NetworkError(AppError): pass
def connect(host):
if not host:
raise NetworkError("Host cannot be empty")
if host == "bad-host":
raise NetworkError(f"Cannot reach {host}")
return f"Connected to {host}"
for host in ["db.example.com", "", "bad-host"]:
try:
print(connect(host))
except NetworkError as e:
print(f"Network error: {e}")
▶ Output
Connected to db.example.com
Network error: Host cannot be empty
Network error: Cannot reach bad-host📝 Exercises
Try them yourself first, then open the solution to compare.
Exercise 1
Robust Input Loop
Goal: Write a function get_positive_int(prompt) that keeps asking until valid.
Requirements
- Catches ValueError for non-integers
- Validates that the value is positive
- Returns the valid integer
▶Toggle solution
SOLUTION
def get_positive_int(prompt):
while True:
try:
n = int(input(prompt))
if n <= 0:
raise ValueError("must be positive")
return n
except ValueError as e:
print(f" Invalid: {e}. Try again.")
n = get_positive_int("Enter a positive integer: ")
print(f"Got: {n}")
Example code / lecture materials
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗