Warming up the neural circuits...
By the end of this chapter you will:
Errors are data — ignoring them is a design decision too. Every program encounters errors: files that don't exist, networks that timeout, users who type letters where numbers are expected. How you handle these errors separates toy code from production code.
Python's philosophy is EAFP: "Easier to Ask Forgiveness than Permission." Instead of checking every condition upfront, try the operation and handle the exception if it fails.
# Specific exception
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero")
# Multiple exceptions
try:
value = int("abc")
except ValueError:
print("Invalid number")
except TypeError:
print("Wrong type")
# Catch multiple in one block
try:
value = int("abc")
except (ValueError, TypeError) as e:
print(f"Error: {e}")
# Get exception object
try:
value = int("abc")
except ValueError as e:
print(f"Error: {e}") # Error: invalid literal for int()Rule: Always catch specific exceptions. Never use bare except: — it catches everything, including KeyboardInterrupt.
try:
file = open("data.txt")
data = file.read()
except FileNotFoundError:
print("File not found")
else:
# Runs only if try succeeded
print(f"Read {len
| Clause | Runs when |
|---|---|
try | Always (attempt) |
except | Exception occurred |
else | No exception |
finally | Always (cleanup) |
Use case for finally: Close files, release locks, disconnect from databases.
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
# Chaining exceptions
try:
value = int("abc")
except ValueError
Best practices:
from when wrapping# Base exception for your app
class AppError(Exception):
pass
# Specific exceptions
class ValidationError(AppError):
pass
class NotFoundError(AppError):
pass
# Usage
def get_user(user_id):
if
Hierarchy:
AppError
├── ValidationError
├── NotFoundError
└── PermissionError# LBYL: Look Before You Leap
if key in dictionary:
value = dictionary[key]
else:
value = default
# EAFP: Easier to Ask Forgiveness
try:
value = dictionary[key]
except KeyError:
value = default
#
| Style | Pros | Cons |
|---|---|---|
| EAFP | Cleaner, faster for success | Exception overhead on failure |
| LBYL | Explicit, no exceptions | Race conditions, verbose |
Python prefers EAFP — it's more readable and often faster.
The exception flow pattern is: try (attempt) → except (handle failure) → else (success only) → finally (always cleanup).
| Mistake | Why it's wrong | Fix |
|---|---|---|
except: | Catches everything, including KeyboardInterrupt | except Exception: or specific |
except Exception as e: pass | Silently swallows errors | At least log the error |
try with too much code | Hard to know what failed | Keep try blocks small |
raise Exception("error") | Generic, unhelpful | Use specific exception + message |
except:?raise from.Beginner:
"What's the difference between except: and except Exception:?"
except:catches everything, including KeyboardInterrupt and SystemExit.except Exception:catches only regular exceptions (not system exits). Always useexcept Exception:or specific exceptions.
"When does the else clause run?"
The
elseclause runs only if no exception was raised in thetryblock. It's useful for code that should only execute on success.
Senior:
"What is EAFP and when would you use it?"
EAFP (Easier to Ask Forgiveness than Permission) is Python's preferred error handling style. Instead of checking conditions first (LBYL), try the operation and handle the exception. It's cleaner and often faster for success cases.
"How do you design a custom exception hierarchy?"
try/except catches exceptions. Always catch specific exceptions.else runs on success. finally always runs (cleanup).raise signals errors. raise from chains exceptions.I want to…
├── Catch error → try: ... except SpecificError:
├── Cleanup → finally:
├── Success only → else:
├── Signal error → raise ValueError("message")
├── Chain → raise NewError from old_error
└── Custom → class MyError(Exception): passWhen does the 'else' clause run in try/except/else/finally?
Create a base exception class for your app (e.g.,
AppError). Create specific exceptions that inherit from it (ValidationError, NotFoundError). This allows callers to catch specific errors or all app errors with one handler.