Module 15 β Error & Exception Handling
Anticipating what can go wrong, and failing gracefully instead of crashing.
Prerequisite: Module 14 β Object-Oriented Programming
After this lesson, you will be able to
- Catch and handle errors with try/except
- Use else and finally correctly alongside try/except
- Raise your own exceptions with raise
- Recognize common built-in error types
Concept & Syntax
An exception is Python's way of saying "something went wrong, and I don't know how to
continue." Without handling, an exception crashes your whole program. try/except lets
you anticipate specific failures β bad user input, a missing file, a network timeout β and respond sensibly
instead of crashing.
try:
risky_code()
except SomeError as e:
handle_it(e)
else:
only_if_no_error()
finally:
always_runs()Examples
Basic try/except Very Easy
try:
age = int("not a number")
except ValueError:
print("That wasn't a valid number.")Instead of crashing, the program catches the ValueError and responds with a helpful message.
Catching the error object Easy
try:
result = 10 / 0
except ZeroDivisionError as e:
print("Error:", e)as e captures the exception object itself, letting you inspect or log its message.
else and finally Intermediate
try:
number = int("42")
except ValueError:
print("Invalid input")
else:
print("Conversion succeeded:", number)
finally:
print("Done attempting conversion.")else runs only if no exception occurred; finally always runs, whether or not there was an error β useful for cleanup.
Raising your own exception Real World
def withdraw(balance, amount):
if amount > balance:
raise ValueError("Insufficient funds")
return balance - amount
try:
withdraw(50, 100)
except ValueError as e:
print("Transaction failed:", e)raise lets your own code trigger an exception deliberately, with a message explaining what went wrong.
Custom exception classes Challenge
class InsufficientFundsError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(f"Tried to withdraw {amount}, only {balance} available")
return balance - amount
try:
withdraw(50, 100)
except InsufficientFundsError as e:
print(e)A custom exception (a class inheriting from Exception) lets calling code catch your specific error type by name, not just a generic one.
Visual Explanation
π‘ Why does this work?
Read the concept explanation above again slowly, line by line β every keyword there maps directly onto a line of code in the examples. If it still doesn't click, re-run the traced example (if this lesson has one) one step at a time.
π What happens internally? (advanced)
Under the hood, Python compiles your source into bytecode and runs it on a virtual machine (the CPython interpreter). Variables are names in a namespace dictionary pointing at objects in memory β which is exactly what the memory panel in the tracer above is showing you.
Common Mistakes
A bare `except:` catches everything, including typos and unrelated bugs, hiding problems instead of fixing them.
try:
do_something()
except:
passtry:
do_something()
except ValueError as e:
print("Invalid value:", e)- Catch the most specific exception type you can β ValueError, not a bare except.
- Never silently swallow an exception with a bare `pass` β at least log it.
- Use finally for cleanup that must happen regardless of success (closing a file, releasing a resource).
- Raise a clear, specific error message so whoever catches it understands what went wrong.
Exercise & Challenge
Exercise 1 β Debug this code
try:
print(10 / 0)
except ValueError:
print("Cannot divide")This code should catch a division-by-zero error, but the except clause catches the wrong exception type. What should it say instead of ValueError?
π Challenge
Write a function safe_divide(a, b) that returns the division result, or prints a friendly message and returns None if b is zero.
- Use try/except ZeroDivisionError
- Return the computed value on success
- Return None and print a message on failure
π‘ Need a hint?
return None explicitly makes the failure case clear.
π Show a sample solution
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
print("Cannot divide by zero.")
return None
print(safe_divide(10, 2))
print(safe_divide(10, 0))Quiz
Answer every question, then submit to see your score and explanations.
1. What does a bare `except:` (with no error type) catch?
2. When does the else block of a try/except run?
3. When does finally run?
4. What does raise ValueError("message") do?
Summary
- try/except catches exceptions so your program can respond instead of crashing.
- else runs only on success; finally always runs, ideal for cleanup.
- raise lets your own code deliberately trigger an exception with a clear message.
- Always catch the most specific exception type you reasonably can.