Python 101Free
CONTROL FLOW

Exceptions

What happens when things go wrong, and how to recover.

SECTION 01

try and except

Wrap risky code in a try block. If something inside raises an exception, control jumps to the matching except block instead of crashing the program. The except clause names which exception class to catch (or which classes, in a tuple).

A bare except: catches everything, including keyboard interrupts and system exits. Avoid it. If you do not know which exception to catch, catch Exception instead, which leaves the truly fatal ones alone.

The goal is not to suppress every error. The goal is to handle the specific cases you can recover from, and let the rest crash loudly so you can fix them.

python
try:
    n = int(user_input)
except ValueError:
    n = 0   # fall back when the input cannot be parsed
SECTION 02

raise and chaining

raise triggers an exception manually. You pass an exception instance, usually one of the built-in classes (ValueError, KeyError, RuntimeError) or one you defined yourself. The exception travels up the call stack until something catches it, or the program exits.

The from clause links a new exception to an existing one. raise DomainError(...) from e says "this domain error is being raised in response to that lower-level error". The original is preserved as the cause and shows up in the traceback.

Use this pattern when you want to translate an internal error into something more meaningful at your API boundary, without losing the trail. Anyone debugging will see both layers and understand what happened.

python
try:
    data = json.loads(raw)
except ValueError as e:
    raise DataError("bad payload") from e
SECTION 03

finally and cleanup

The finally block runs whether the try block succeeded, failed, or was exited early. It is the place to release a resource that you opened, regardless of what happened in between.

Classic uses are closing a file, releasing a lock, or rolling back a database transaction. The pattern is try: do_work() / except SomeError: handle() / finally: cleanup().

In modern Python this is usually replaced by a with statement, which is nicer to read and harder to get wrong. finally is still the right tool when you need cleanup that does not fit the context manager pattern.

python
f = open("data.txt")
try:
    process(f.read())
finally:
    f.close()   # runs whether process raised or not
← PREVIOUS
Loops