Exceptions: a control path, not a crash
try/except/else/finally routes errors to handlers, and finally is the one block that always runs.
Every non-trivial operation can fail: opening a missing file, parsing text that is not a number, dividing by zero. Language designers before exceptions handled this by returning error codes and hoping every caller checked. Python instead lets an operation raise an exception and unwinds the call stack until a handler catches it — or the program stops with a traceback.
Errors travel upward until caught
A raise is a jump out of the current function and into the nearest enclosing handler for that exception type. If there is none, the interpreter prints the traceback and exits. This is why a function can be written for the happy path and still not lose information when something fails.
The stepper runs a real try block over different inputs, so each press shows
which handler catches the error and whether the else and finally blocks run.
Change the input and step through which handler catches the error
- n
- —
- y
- —
- finally
- pending
enter the try block
Exactly one except clause runs, and Python checks them top to bottom; else runs only when nothing was raised. The finally block is the one guaranteed stop — it runs on every path, including a bare return inside try, which is why cleanup code lives there.
The five clauses
trymarks the guarded block.except SomeErrorcatches that type (and its subclasses), in top-to-bottom order. The first match wins.elseruns only if thetryblock raised nothing.finallyalways runs, on success, on failure, and even on areturninsidetry. This is where cleanup belongs.raisesignals an exception deliberately.
try:
n = int(text)
y = 10 / n
except ValueError:
y = None # text was not a number
except ZeroDivisionError:
y = -1 # n was zero
else:
note = "ok" # only when nothing was raised
finally:
closed = True # alwaysCatch the specific, not everything
An except Exception: that swallows every error will hide the bug you need to
see. Name the types you can actually handle and let the rest propagate. Catching
too much is indistinguishable from ignoring failures.
Order matters: subclasses before superclasses
FileNotFoundError is a subclass of OSError. If you write except OSError:
before except FileNotFoundError:, the specific clause can never run. Python
picks the first matching clause, so list the most specific types first.
Raising and defining errors
Use raise to refuse an operation that cannot be completed correctly:
def withdraw(balance, amount):
if amount > balance:
raise ValueError("insufficient funds")
return balance - amountFor a project's own failure modes, define a small exception class that inherits
from a built-in such as ValueError. Callers can then catch your specific type
without catching unrelated errors of the same base.
Cleanup with context managers
Because finally is easy to forget, Python offers the with statement for
resources that must be released:
with open("notes.txt") as f:
text = f.read()The file is closed when the block exits, even on an exception. Prefer with
over a hand-written try/finally for files, locks and connections.
Illustrative vs real
The stepper models only three outcomes on a fixed micro-program. Real
tracebacks carry a traceback object with the exact file and line of each
frame, raise ... from ... keeps the cause, and exception groups handle
several failures at once. The routing rule — first matching handler, finally
always runs — is exactly what the panel computes.
Check yourself
Eduspheria wiki · Programming & Data Structures, Objects and I/O
0 / 5 answered
From the exam paper
Modeled on NITJ AI-503, End-Sem December 2024
0 / 4 answered
Where next: files and modules — where a program's data lives and how it is organised into importable pieces.