1.3.5 · D5Python Intermediate

Question bank — Exception handling — try, except (specific), else, finally

1,848 words8 min readBack to topic
Figure — Exception handling — try, except (specific), else, finally
Figure — Exception handling — try, except (specific), else, finally
Figure — Exception handling — try, except (specific), else, finally

True or false — justify

finally only runs when an exception was raised.
False — finally runs on every exit path (figure s01, violet box): clean success, caught exception, uncaught exception, or a return/break/continue leaving the block.
else runs whether or not the try succeeded.
False — else (figure s01, orange road) runs only if try finished with no exception; the whole point is to mark the exception-free success path.
except ValueError: also catches a KeyError.
False — a clause catches only its listed class and that class's subclasses (figure s02); KeyError is not a subclass of ValueError, so it is not caught.
except Exception: catches a KeyboardInterrupt (Ctrl-C).
False — in figure s02, KeyboardInterrupt hangs off BaseException, above Exception, so except Exception deliberately lets Ctrl-C through. See The exception class hierarchy — BaseException, Exception, LookupError.
If two except clauses could match, both run.
False — Python runs the first matching clause top-to-bottom and then leaves; at most one handler fires per raised exception.
Putting on_success() inside try behaves identically to putting it in else.
False — inside try, an error thrown by on_success() would be caught by your except, masking a different bug; else shields the success code from the handler.
A return inside finally overrides a return from try.
True — in figure s03, if finally executes its own return, it wins and the try's pending return (or even a pending exception) is discarded. This is a classic silent-bug source, so avoid returning from finally.
except (ValueError, KeyError): means "catch ValueError only if KeyError also occurred".
False — the tuple means "catch either ValueError or KeyError" with one shared handler; it is an OR, not an AND.
An exception raised inside an except block can be caught by a sibling except of the same try.
False — sibling clauses only see exceptions from the try body; a new error raised inside a handler propagates outward (unless an enclosing try wraps it).
finally runs even if the except clause itself raises a new exception.
True — finally executes before control leaves the construct regardless of what the handler did, including raising again.

Spot the error

try:
    d["k"]
except Exception:
    print("a")
except KeyError:
    print("b")
What is wrong?
The parent Exception (a high box in figure s02) is listed first, so it shadows the child KeyError; the second clause is dead code. Order specific-child clauses before general-parent ones.
try:
    risky()
except:
    pass
Why is this dangerous?
A bare except: swallows everything — including Ctrl-C, SystemExit, and typos like NameError — and pass throws the object away, so real bugs vanish silently. Catch the narrowest class and at least log it (see Logging vs printing errors).
try:
    n = int(s)
    save(n)
except ValueError:
    print("bad number")
What subtle risk hides here?
save(n) sits inside try, so a ValueError raised inside save would be misreported as "bad number". Move save(n) into an else block (the orange road in figure s01) so only int(s) is guarded.
try:
    f = open("x.txt")
    data = f.read()
finally:
    f.close()
Why can finally crash here?
If open itself fails, f was never assigned, so finally hits a NameError on f.close(). Either open before the try, or use a with statement which handles this correctly.
try:
    process()
except Exception as e:
    raise ValueError("failed")
What debugging pain does this cause?
Raising a fresh error discards the original traceback chain unless you write raise ValueError("failed") from e. See Raising exceptions — raise and custom exception classes and Debugging and tracebacks.
except ValueError, KeyError:
    handle()
Why is this a syntax error in Python 3?
The comma form is Python 2 syntax. Modern Python requires a tuple: except (ValueError, KeyError):. Without parentheses the parser cannot tell "two types" from "as-name".

Why questions

Why prefer exceptions over returning an error code from every function?
Error codes force every caller to check and forward them, drowning the real logic; exceptions keep the happy path clean and let error handling live in one place off to the side.
Why does catching a parent class also catch its children?
Matching is done by isinstance(E, ClauseType), and an instance of a child class is an instance of its parent (figure s02), so the parent clause returns true for it.
Why should the try region be as small as possible?
The smaller the guarded region, the less code can accidentally trigger a handler meant for one specific failure, reducing the chance of masking an unrelated bug.
Why does finally run before the value of a try's return is delivered?
return sets a pending result but does not immediately leave (figure s03); finally is guaranteed cleanup, so Python runs it first, then completes the return — this is exactly what makes it reliable for closing resources.
Why is except Exception safer than a bare except:?
except Exception skips BaseException-level signals like KeyboardInterrupt and SystemExit (the top box in figure s02), so Ctrl-C and clean shutdown still work while ordinary errors are handled.
Why bind the exception with as e even if you only print it?
The object e carries the message, type, and traceback context needed for meaningful logging; without it you often lose the reason the error happened.
Why can else reduce silent bugs compared to a big try?
Code in else is unprotected, so if it fails the exception is not swallowed by your handler — it surfaces loudly, exposing the new bug instead of hiding it.

Edge cases

What runs if the try body is empty of risky work and nothing raises, but a finally exists?
try completes, else (if present) runs, then finally runs — the orange road plus violet toll booth in figure s01; finally executes on the clean path too, not only on errors.
What happens if an exception is raised inside the finally block itself?
The new exception replaces whatever was in flight and propagates outward; any pending exception or return from try/except is lost, which is why finally should stay simple.
If try raises E and no except matches, does else run?
No — else runs only when try had no exception. Here finally still runs, then E propagates up the call stack.
What if break is used inside a try that has a finally, all inside a loop?
finally runs first (figure s03 — the detour applies to break just like return), then the break takes effect and exits the loop — cleanup is never skipped by loop-control statements.
Does re-raising with a bare raise inside an except preserve the original traceback?
Yes — a bare raise inside a handler re-throws the same exception with its original traceback intact, unlike raise SomeError(...) which starts fresh.
What happens when a matching except clause finishes normally — does the exception "come back"?
No — once a clause handles the exception it is considered resolved; execution continues after the whole try/except/else/finally construct (after running finally).
Zero-except case: a try/finally with no except at all — is that legal?
Yes — try/finally (no handlers) is valid; any exception still propagates outward, but finally is guaranteed to run first. This is the pattern with generalises.

Recall One-line survival summary

Guard the minimum in try, order excepts child-first then parent (specific classes low in the tree of figure s02 before general classes above them), never bare-except, let else hold the success code, and treat finally as unconditional cleanup that can silently override returns.

Connections

Concept Map

error raised

no error

try block risky code

except SpecificError

else success only

finally always runs

leave construct