What you'll learn
Things go wrong: files vanish, users type nonsense, networks drop. Exceptions are Python's way of signaling and handling those failures without your whole program crashing. Handling them well is the difference between a fragile script and robust software.
By the end you'll be able to:
- Distinguish syntax errors from exceptions and read the hierarchy
- Use
try,except,else, andfinally - Catch specific exceptions and
raiseyour own - Write in the Pythonic EAFP style
Errors & the hierarchy
A syntax error means Python can't even parse your code — it never runs. An exception happens during execution: the code is valid, but something went wrong (dividing by zero, a missing key). Exceptions form a class hierarchy — ValueError, KeyError, FileNotFoundError, and the rest all inherit from Exception — which is why you can catch them broadly or narrowly.
try / except / else / finally
The full structure has four parts, each with a clear job:
try— the code that might fail.except— runs if a matching exception occurs.else— runs only if no exception occurred.finally— always runs, for cleanup, error or not.
def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("Can't divide by zero!")
return None
else:
print("Division succeeded") # runs only if no exception
return result
finally:
print("Done") # ALWAYS runs, error or not
print(safe_divide(10, 2))
print(safe_divide(10, 0))Catching specific errors
Catch the specific exceptions you expect, not a blanket one. Different problems can get different handling, and you avoid accidentally swallowing bugs you didn't anticipate:
def parse_age(text):
try:
return int(text)
except ValueError: # bad number format
return "Not a valid number"
except TypeError: # wrong type entirely
return "Expected a string"
print(parse_age("42"))
print(parse_age("abc"))
print(parse_age(None))Watch out
except: (or except Exception) that catches everything and hides the problem — the infamous "silent failure." Catch what you can handle; let unexpected errors surface so you can find and fix them.raise & custom exceptions
Use raise to signal an error yourself. For domain-specific failures, define your own exception class by subclassing Exception — it makes errors self-documenting and lets callers catch exactly your case:
class InsufficientFundsError(Exception):
"""Raised when a withdrawal exceeds the balance."""
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(f"Cannot withdraw {amount}; balance is {balance}")
return balance - amount
try:
withdraw(100, 250)
except InsufficientFundsError as e:
print("Error:", e)Tip
raise NewError(...) from original to preserve the original cause (exception chaining). The traceback then shows both errors — invaluable when debugging why something failed.EAFP vs LBYL
Two philosophies. LBYL ("Look Before You Leap") checks conditions first; EAFP ("Easier to Ask Forgiveness than Permission") just tries the operation and handles the exception if it fails. Python leans EAFP — it's often cleaner and avoids race conditions (the thing you checked could change before you use it):
data = {"name": "Ada"}
# LBYL — "Look Before You Leap"
if "age" in data:
print(data["age"])
else:
print("no age (LBYL)")
# EAFP — "Easier to Ask Forgiveness than Permission" (Pythonic)
try:
print(data["age"])
except KeyError:
print("no age (EAFP)")Recap & quick check
Key takeaways
- Syntax errors stop parsing; exceptions occur at runtime and can be caught.
- try runs risky code; except handles failures; else runs on success; finally always runs (cleanup).
- Catch specific exceptions — avoid bare except that hides bugs.
- raise signals errors; subclass Exception for clear, catchable domain-specific errors; chain with 'from'.
- Python favors EAFP (try/except) over LBYL (pre-checks) — often cleaner and race-free.
Quick check
1. Which block always runs, whether or not an exception occurred?
2. Why catch specific exceptions instead of a bare except?
3. How do you signal an error yourself?
4. How do you define a custom exception?
5. What does EAFP stand for?
Your programs handle failure gracefully now. Next we work with the outside world: Module 21 — Files, Paths & I/O.