Error handling with try/except
The basic shape — and how it compares
Most languages you’ve likely used have some form of this same pattern —
Java/C# call it try/catch, JS is nearly identical to Python’s spelling:
try {
result = 10 / 0;
} catch (ArithmeticException e) {
result = -1;
}Python’s version:
Same idea, different keyword (except instead of catch), and Python names
the exception type directly after except rather than in parentheses.
Seeing the crash first
Without a try, an error stops the program entirely — you’ve already seen
this in Concept 1’s execution-model example. Here’s the same idea,
specifically for division:
Wrapping the risky line in try/except lets you handle it instead of
crashing:
The try block runs normally. If the specific error named in except
occurs anywhere inside it, execution jumps straight to that except block —
everything else remaining in try is skipped.
Catching specific errors vs. catching everything
You can name the exact error type you expect (recommended), or catch
everything with a bare except::
This runs — but it’s hiding the actual problem: divide(10, "two") fails
because you can’t divide by a string (TypeError), not because of division
by zero. A bare except: swallows that distinction, which makes bugs harder
to find later. Naming the specific type keeps you honest about what you’re
actually expecting to go wrong:
Multiple except clauses can follow one try, each catching a different
error type.
Order matters with multiple except clauses
Python checks except clauses top to bottom and uses the first one that
matches — same as elif. This matters when exception types overlap:
ZeroDivisionError is actually a subclass of ArithmeticError, so if you
catch the broader type first, the more specific one never gets a chance to
run.
The second except isn’t wrong, it’s just unreachable for this case. The
fix is ordering specific exceptions before general ones:
finally — runs no matter what
A finally block runs whether the try succeeded, failed, or was caught —
useful for cleanup that must happen regardless (closing a file, releasing a
connection):
Notice "divide attempt finished" prints before the return value in each
case: finally runs after the try/except resolves, but before the
function actually returns to the caller.
Reading a traceback
You’ve seen a few tracebacks already in this lesson without a formal breakdown — here’s what’s actually in one:
Traceback (most recent call last):
File "script.py", line 4, in <module>
print(divide(10, 0))
File "script.py", line 2, in divide
return a / b
ZeroDivisionError: division by zeroRead it as a call stack, top to bottom: line 4 called divide, which failed
on line 2. The last line is the one that matters most day-to-day — it
names the exception type and the specific message. When something breaks,
start there, then use the lines above it to trace where the call came from
if the error itself isn’t enough context.
What's the main syntax difference between Python's `try`/`except` and Java/C#'s `try`/`catch`?
If `message` isn't a string, checking `"calculate" in message` raises a `TypeError`. Wrap the function so that case returns `"invalid input"` instead of crashing, while valid strings still route normally through the existing if/elif chain. (Composes Concept 2's control flow with Concept 3's try/except.)