Raising Exceptions
Signalling problems from your own code.
4 minute read
Big idea
Your own code can raise exceptions too: raise ValueError("..."). It's how a function says "I can't do this with what you gave me" — loudly, so the problem can't be ignored.
raise
def set_age(age):
if age < 0:
raise ValueError("age can't be negative")
return age
print(set_age(30)) # 30
print(set_age(-5)) # stops the program
The first call prints 30. The second stops the program with ValueError: age can't be negative — nothing after it runs. Pick the built-in exception that fits best: ValueError for a bad value, TypeError for the wrong type.
Raising and catching together
The function raises; the caller decides what to do:
def withdraw(balance, amount):
if amount > balance:
raise ValueError("insufficient funds")
return balance - amount
balance = 100
for amount in [30, 500, 50]:
try:
balance = withdraw(balance, amount)
print("OK, balance", balance)
except ValueError as e:
print("Refused:", e)What does it print? Show the answer
OK, balance 70
Refused: insufficient funds
OK, balance 20Run and edit it in the app.
Why raise instead of printing an error or returning -1? Because a returned -1 can be silently used as if it were a real balance. An exception can't be ignored by accident — if nobody catches it, the program stops and says why.
Your own exception types
For errors specific to your program, make a new exception class by building on Exception (classes come in the OOP module — this is the whole recipe):
class OutOfStock(Exception):
pass
def buy(stock, item):
if stock.get(item, 0) == 0:
raise OutOfStock(item)
stock[item] -= 1
stock = {"tea": 1}
for item in ["tea", "tea"]:
try:
buy(stock, item)
print("Bought", item)
except OutOfStock as e:
print("Sorry, out of", e)output
Bought tea
Sorry, out of teaRun and edit it in the app.
assert
assert condition, "message" raises an AssertionError if the condition is False. It's for checking your own assumptions while developing — not for validating user input.
assert len(scores) > 0, "scores should never be empty here"
def safe_ratio(a, b):
if b == 0:
print("b must not be zero")
return a / b
try:
safe_ratio(1, 0)
except ValueError as e:
print("Caught:", e)output
Caught: b must not be zeroRun and edit it in the app.
Printing doesn't stop the function, so it carries on to a / 0 and raises a ZeroDivisionError the caller isn't expecting. Replace the print with raise ValueError("b must not be zero") — then the caller's except ValueError catches it and shows the message.
Why raise an exception rather than return an error value like -1?
Watch it
Think of it like
A cashier who spots a fake banknote doesn't quietly hand back the wrong change — they stop and call the manager. Raising is calling the manager; except is the manager deciding what happens next.
Key terms
- raise — Signal an exception from your own code.
- Custom exception — Your own exception class, built on
Exception. - assert — Raises
AssertionErrorif a condition you expected is False.
Check yourself
Which line raises an exception?
Which exception fits "the value is out of range"?
How do you make your own exception type?