Exception Handling
Python · Core Language · Run with python3
Explanation
Python exceptions are objects that inherit from `BaseException`. The hierarchy: `BaseException` → `Exception` → specific exceptions like `ValueError`, `TypeError`, `IOError`. The `try`/`except`/`else`/`finally` structure: `except` catches errors, `else` runs only if no exception occurred, `finally` always runs. Python 3.11+ added `ExceptionGroup` for handling multiple simultaneous exceptions (from `asyncio.TaskGroup`).
Real-World Scenario
Parsing user input: catch `ValueError` for bad numbers, `KeyError` for missing dict keys. Always be specific — catching bare `except:` or `except Exception:` hides bugs. Define custom exceptions by inheriting from `Exception` to add domain-specific error types. ```python class InsufficientFundsError(ValueError): def __init__(self, balance: float, amount: float): self.balance = balance self.amount = amount super().__init__(f"Need {amount:.2f} but only have {balance:.2f}") def safe_divide(a, b): try: result = a / b except ZeroDivisionError: return None except TypeError as e: raise ValueError(f"Arguments must be numbers: {e}") from e else: return result finally: pass # cleanup always runs print(safe_divide(10, 2)) # 5.0 print(safe_divide(10, 0)) # None try: raise InsufficientFundsError(50.0, 100.0) except InsufficientFundsError as e: print(e) # Need 100.00 but only have 50.00 ```
Quick Snippet
class InsufficientFundsError(ValueError):
def __init__(self, balance: float, amount: float):
self.balance = balance
self.amount = amount
super().__init__(f"Need {amount:.2f} but only have {balance:.2f}")
def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
return None
except TypeError as e:
raise ValueError(f"Arguments must be numbers: {e}") from e
else:
return result
finally:
pass # cleanup always runs
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # None
try:
raise InsufficientFundsError(50.0, 100.0)
except InsufficientFundsError as e:
print(e) # Need 100.00 but only have 50.00Full Example
# Compile: python3 exception_handling.py
# Output:
# 5.0
# None
# Need 100.00 but only have 50.00
class InsufficientFundsError(ValueError):
def __init__(self, balance: float, amount: float):
self.balance = balance
self.amount = amount
super().__init__(f"Need {amount:.2f} but only have {balance:.2f}")
def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
return None
except TypeError as e:
raise ValueError(f"Arguments must be numbers: {e}") from e
else:
return result # only runs if no exception
finally:
pass # always runs
print(safe_divide(10, 2))
print(safe_divide(10, 0))
try:
raise InsufficientFundsError(50.0, 100.0)
except InsufficientFundsError as e:
print(e)
# Exception chaining
try:
try:
int("not-a-number")
except ValueError as e:
raise RuntimeError("Config parsing failed") from e
except RuntimeError as e:
print(f"Caught: {e}")
print(f"Caused by: {e.__cause__}")
python3 exception_handling.py