Topic 8 of 64
Exception Handling
Overview
Python uses try/except/else/finally blocks to handle errors gracefully. Writing robust exception handling is what separates production code from scripts — it prevents crashes and gives meaningful error messages.
Syntax
python
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"Math error: {e}")
except (TypeError, ValueError) as e:
print(f"Value error: {e}")
except Exception as e:
print(f"Unexpected: {e}")
raise # re-raise the exception
else:
print("No exception occurred") # runs if no exception
finally:
print("Always runs") # cleanup code
# Custom exceptions
class InsufficientFundsError(Exception):
def __init__(self, amount, balance):
self.amount = amount
self.balance = balance
super().__init__(f"Cannot withdraw ₹{amount}. Balance: ₹{balance}")
# Raising exceptions
if amount <= 0:
raise ValueError("Amount must be positive")Common Pitfalls
- Never use bare except: — it catches even KeyboardInterrupt and SystemExit, masking serious errors.
- except Exception is generally the safest broad catch — it doesn't catch BaseException subclasses like SystemExit.
- Interview tip: The else block in try/except runs only if NO exception was raised — useful for code that should only run on success.
Real-World Example
A robust payment processing function with custom exceptions:
example
python
class PaymentError(Exception): pass
class InsufficientFundsError(PaymentError): pass
class CardDeclinedError(PaymentError): pass
def process_payment(user_id: int, amount: float, card_token: str) -> dict:
try:
if amount <= 0:
raise ValueError(f"Invalid amount: {amount}")
account = db.get_account(user_id)
if account.balance < amount:
raise InsufficientFundsError(amount, account.balance)
result = payment_gateway.charge(card_token, amount)
if result["status"] == "declined":
raise CardDeclinedError(f"Card declined: {result['reason']}")
return {"success": True, "transaction_id": result["id"]}
except InsufficientFundsError as e:
logger.warning(f"Insufficient funds for user {user_id}: {e}")
return {"success": False, "error": "insufficient_funds", "message": str(e)}
except CardDeclinedError as e:
logger.error(f"Card declined for user {user_id}: {e}")
return {"success": False, "error": "card_declined"}
except Exception as e:
logger.critical(f"Unexpected payment error: {e}", exc_info=True)
raise # Re-raise unexpected errors
finally:
db.session.close() # always close DB session