Functions, modules, and error handling · Lesson 2 of 3
Exceptions and defensive error handling
Raise and catch specific exceptions at the layer that can respond meaningfully.
Lesson
1. Concept and mental model
Exceptions communicate that an operation could not complete as requested. Python raises exceptions for conditions such as invalid numeric conversion, a missing dictionary key, or a failed file operation. Catch the narrow exception that you can handle, such as ValueError when parsing user input. A broad except Exception can accidentally hide programming bugs and should not be used as a routine substitute for understanding failure modes.
2. How it works
Use try for the operation that may fail, except for the recovery you can actually provide, and else for work that should happen only when the operation succeeds. A finally block is useful for cleanup that must happen regardless of success; context managers such as with open(...) usually manage resources more clearly. When the current layer cannot recover, let the exception propagate or raise a domain-specific exception with useful context using raise ... from error.
3. Use it safely and effectively
Validate at system boundaries and report actionable messages without exposing secrets, raw credentials, or unnecessary internal details. Do not use exceptions for ordinary branching when a simple condition expresses the rule. Test both success and failure paths, including the exception type and expected user-facing behavior. A reliable program defines how it handles bad input, unavailable files, timeouts, and partial operations.
4. Syntax reference
try:
quantity = int('12')
except ValueError as error:
print(f'Invalid quantity: {error}')
else:
print(quantity * 2)5. Guided walkthrough
Put only the operation that may fail inside the try block, catch an exception your code can recover from, and give the caller a specific message or result. If you cannot safely recover, preserve the failure so the layer with enough context can respond.
6. Common mistakes to avoid
- Catching every exception and continuing with a fake success value.
- Putting unrelated operations inside one large try block, which makes it unclear which operation failed.
Visual guide
Handle an operation safely
-
Step 01
Attempt
Run the operation that may fail.
-
Step 02
Recover
Catch a specific recoverable exception.
-
Step 03
Report
Return a clear result or propagate failure.
Code examples
try:
quantity = int('12')
except ValueError as error:
print(f'Invalid quantity: {error}')
else:
print(quantity * 2)
24
raw_value = '12x'
try:
value = int(raw_value)
except ValueError:
print('Enter a whole number.')
Enter a whole number.
def withdraw(balance: float, amount: float) -> float:
if amount <= 0:
raise ValueError('Amount must be positive')
if amount > balance:
raise ValueError('Insufficient funds')
return balance - amount
Returns the remaining balance or raises ValueError.
Key points
- Raise and catch specific exceptions at the layer that can respond meaningfully.
- Prefer explicit, testable behavior over hidden side effects.
- Validate assumptions at system boundaries and handle failure deliberately.
Check your understanding
Practice
Your learning progress