So far, you have learned to catch errors with
Instead of silently returning a bad value or crashing later, Python lets you stop execution immediately by raising your own exception. This tells every caller, "the input was invalid; I am giving up." The message you attach to the exception becomes part of the traceback.
try and except. But sometimes the error is not a surprise. It is something you know about in advance: an argument that cannot be right.Instead of silently returning a bad value or crashing later, Python lets you stop execution immediately by raising your own exception. This tells every caller, "the input was invalid; I am giving up." The message you attach to the exception becomes part of the traceback.
raise with a clear message
def divide(a, b):
if b == 0:
raise ValueError("b must not be zero")
return a / b
# Calling divide(10, 0) raises ValueError with the message above.
print(divide(10, 2)) # works fine -> 5.0raise takes an exception object. The most common built-in types are ValueError, TypeError, and KeyError. Pick the one whose name best describes what went wrong, then pass a short human-readable string. That string is what shows up in tracebacks, so write it like you would explain to another developer.Validating arguments
def set_temperature(celsius):
if not isinstance(celsius, (int, float)):
raise TypeError("celsius must be a number")
if celsius < -273.15 or celsius > 10_000:
raise ValueError("temperature out of plausible range")
return f"{celsius} C"
print(set_temperature(20)) # 20 C
# set_temperature('hot') -> TypeError with a clear messageLetting it travel up
def inner(x):
if x < 0:
raise ValueError("inner: negative not allowed")
return x * 2
def outer(x):
# No try/except here. The exception keeps climbing.
result = inner(x) + 1
return result
# If you call outer(-5), the traceback will show BOTH frames:
# File ..., in outer: result = inner(x) + 1
# File ..., in inner: raise ValueError(...)You do not have to catch an exception at the level where it is raised. It bubbles up through
inner, then outer, and so on, all the way to a top-level try/except or into your test runner's output. This keeps low-level functions simple: they validate their own inputs and let higher layers decide how (or whether) to recover.