Wrap risky code in a try block, and handle the failure in an except block:
try:
result = 10 / 0
except ZeroDivisionError:
result = None
print("can't divide by zero")
If nothing goes wrong, the except block never runs. If a ZeroDivisionError is raised anywhere inside the try block, execution jumps straight to the matching except and continues from there — the program doesn't crash.
Always name the exception type you expect. except ZeroDivisionError: only catches that one type — anything else still propagates and crashes, which is what you want: an unexpected bug shouldn't be silently swallowed.
try:
age = int(user_input)
except ValueError:
print("that's not a number")
Use as e to inspect the exception itself — its message is available via str(e):
try:
age = int(user_input)
except ValueError as e:
print(f"invalid input: {e}")
You're not limited to catching exceptions Python raises for you — use raise to signal a problem yourself:
def withdraw(balance, amount):
if amount > balance:
raise ValueError("insufficient funds")
return balance - amount
Calling code can then catch it exactly like a built-in exception:
try:
balance = withdraw(balance, 500)
except ValueError as e:
print(f"withdrawal failed: {e}")
That's the core pattern you'll use throughout this section: some code that might fail, a try around it, and an except naming exactly what you're prepared to handle.