Every pattern you'll actually write, in the order you'll need them. The one rule above all: catch the specific error, not everything.
# 1. the standard shape
try:
age = int(user_input)
except ValueError:
print("that wasn't a number")
# 2. use the error object
try:
data = json.loads(text)
except json.JSONDecodeError as e:
print(f"bad JSON at line {e.lineno}: {e.msg}")
# 3. different handling per error type
try:
resp = requests.get(url, timeout=5)
except requests.Timeout:
retry() # timeouts are retryable
except requests.ConnectionError:
alert_ops() # network down is not
# 4. same handling for several types
except (ValueError, KeyError) as e:
log(e)
try:
f = open(path, encoding="utf-8")
except FileNotFoundError:
print("no such file")
else:
# runs ONLY if no exception — keeps the try block minimal,
# so you don't accidentally catch errors from OTHER lines
process(f.read())
f.close()
finally:
# runs NO MATTER WHAT — cleanup lives here
release_lock()
# refuse bad input loudly
def set_age(age):
if age < 0:
raise ValueError(f"age can't be negative, got {age}")
# log-and-rethrow: observe the error without swallowing it
try:
charge_card(order)
except PaymentError:
log_incident(order)
raise # bare raise = same error continues up
# custom exception for your domain (it's just a class)
class QuotaExceeded(Exception): pass
raise QuotaExceeded("key used 10,000 calls today")
for attempt in range(3):
try:
result = flaky_api_call()
break # success → exit loop
except requests.Timeout:
if attempt == 2:
raise # out of retries → let it fail loudly
time.sleep(2 ** attempt) # 1s, 2s — backoff
except: (bare) and except Exception: pass swallow everything — including the typo you just made, KeyboardInterrupt, and the real bug you'll spend Friday hunting. The symptom: "my code runs but does nothing." If you truly must catch broadly (a top-level loop that can't die), log the full error: except Exception as e: logger.exception(e) — never pass.