Exception Handling
Python's try/except/else/finally handles errors elegantly. The else clause — unique to Python — runs only on success. Custom exceptions make errors self-documenting.
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero.")
finally:
print("This always runs.") # cleanup goes here
# Multiple except — handle different exceptions differently:
def safe_parse_mark(text):
try:
mark = float(text)
if not (0 <= mark <= 100):
raise ValueError(f"Mark {mark} is outside valid range 0-100")
return mark
except ValueError as e:
print(f"Invalid mark input: {e}")
return None
except TypeError as e:
print(f"Wrong type: {e}")
return None
print(safe_parse_mark("74.5")) # 74.5
print(safe_parse_mark("abc")) # ValueError
print(safe_parse_mark("150")) # ValueError — out of range
print(safe_parse_mark(None)) # TypeError
# try/except/else/finally:
try:
value = int("42")
except ValueError:
print("Conversion failed.")
else:
# Only runs if NO exception was raised
print(f"Converted successfully: {value}")
finally:
print("Always runs.")
# Output:
# Converted successfully: 42
# Always runs.
Custom Exceptions
class InsufficientFundsError(Exception):
"""Raised when a withdrawal exceeds the account balance."""
def __init__(self, shortfall: float):
self.shortfall = shortfall
super().__init__(f"Insufficient funds. Short by R{shortfall:.2f}.")
class InvalidMarkError(ValueError):
"""Raised when a mark is outside the valid 0-100 range."""
def __init__(self, mark):
super().__init__(f"Mark must be 0-100. Got: {mark}")
# Using custom exceptions:
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(amount - balance)
return balance - amount
try:
withdraw(500, 1200)
except InsufficientFundsError as e:
print(e)
print(f"You need R{e.shortfall:.2f} more.")
Context Managers and Exception Safety
# The with statement is Python's try-finally for resources:
# This:
with open("file.txt") as f:
data = f.read()
# Is equivalent to:
f = open("file.txt")
try:
data = f.read()
finally:
f.close() # guaranteed even on exception
Practice Task
Your Turn
Write a LearnerDatabase class with custom exceptions DatabaseFullError (max 50 learners), LearnerNotFoundError, DuplicateIdError. Implement add_learner(), get_learner(id), remove_learner(id), update_mark(id, mark). Each method raises the appropriate custom exception. Test all exception paths in a main script, catching each and printing a helpful message.
Common Mistakes
- Bare
except:catches SystemExit and KeyboardInterrupt — always specify the exception type. - Empty except blocks hide bugs completely.
- Raising a new exception inside except without
raise ... from eloses the original traceback. - Using exceptions for normal control flow — they are expensive and for exceptional conditions.
Professional Tip
When you catch an exception and want to add context then re-raise, use: raise RuntimeError('Loading failed') from original_error. This preserves both the original and new exception in the traceback.
Mini Quiz
Which clause runs only when the try block completed without exception?