Raising your own exceptions
Your own functions can raise exceptions too. It's how a function says "I can't do what you asked, and here's why", instead of silently returning a wrong answer.
raise
def set_age(age):
if age < 0:
raise ValueError(f"Age can't be negative (got {age})")
print("Age set to", age)
set_age(30)
set_age(-5)The function stops at raise, and the caller receives the exception, just like a built-in one.
Catching what you raise
def withdraw(balance, amount):
if amount <= 0:
raise ValueError("Amount must be positive.")
if amount > balance:
raise ValueError(f"Insufficient funds: balance is {balance}.")
return balance - amount
for amount in [50, -10, 500]:
try:
print("New balance:", withdraw(200, amount))
except ValueError as e:
print("Refused:", e)Raising in the function and catching in the caller keeps the two jobs apart: the function detects the problem, the caller decides what to do about it.
Choosing the exception type
Pick the built-in exception that matches the problem:
ValueErrorfor a bad value,TypeErrorfor the wrong type,KeyError/IndexErrorfor missing items,RuntimeErrorwhen nothing else fits.
Your own exceptions
For errors specific to your program, define a class that inherits from Exception:
class InsufficientFundsError(Exception):
"""Raised when a withdrawal is larger than the balance."""
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(f"Need {amount - balance} more.")
return balance - amount
try:
withdraw(100, 180)
except InsufficientFundsError as e:
print("Payment failed:", e)Custom exceptions make except blocks precise: callers can catch exactly your error and nothing else.
Re-raising
Sometimes you want to note an error and still pass it on. A bare raise inside except re-raises the same exception:
def load_setting(settings, key):
try:
return settings[key]
except KeyError:
print(f"[log] setting {key!r} is missing")
raise
try:
load_setting({"theme": "dark"}, "language")
except KeyError:
print("The caller handled it.")assert: checks for programmers
assert raises an AssertionError when a condition is false. It's for catching bugs during development, not for validating user input:
def average(numbers):
assert len(numbers) > 0, "average() needs at least one number"
return sum(numbers) / len(numbers)
print(average([2, 4, 6]))
print(average([]))