How does Python choose which except clause handles a raised exception?
answer
- Top to bottom, not most specific
- Subclass counts, so families match
- Only one clause ever runs
- Parentheses, not brackets, for several types
- A broad clause above hides a narrow one
basics
~20 sPython tests the except clauses top to bottom and runs the first whose named class is the raised exception's class or a base of it. Only that clause runs; a parenthesized tuple matches any of several types.
solid answer
~40 sMatching is first-match-wins in source order, not best-match. Python compares the raised exception's class against each clause's type: a clause matches if the class is that type or a subclass of it. The first matching clause runs and the rest are skipped, so a broad clause placed above a narrow one makes the narrow one dead code — `except Exception` before `except ValueError` means the `ValueError` body never runs, and it is not a syntax error, just a silent bug that a linter can flag. One clause can name a parenthesized tuple, `except (ValueError, TypeError) as err:`, which matches any member; the members must all be `BaseException` subclasses or Python raises `TypeError: catching classes that do not inherit from BaseException is not allowed` when the match is attempted.
code
python · 17 linesdef handle(exc, broad_first):
if broad_first:
try:
raise exc
except Exception:
return "broad"
except ValueError:
return "specific (unreachable)"
try:
raise exc
except ValueError:
return "specific"
except Exception:
return "broad"
print(handle(ValueError("x"), broad_first=True))
print(handle(ValueError("x"), broad_first=False))go deeper
Know that clauses are tried in the order you wrote them and that only the first match runs, so put the specific exception types above the general ones.
Be able to explain matching as a subclass test against the raised exception's class, why a broad clause above a narrow one silently kills the narrow one, and how a parenthesized tuple clause handles several types at once.
Show that you group clauses by the recovery they perform rather than by taxonomy, and that you know the tuple-member type check fires at match time — so a bad handler can lurk on a rarely-taken path until the day it is needed.
Own the consistency question: what a codebase's handler chains are expected to look like, which static analysis rules enforce it, and how error taxonomies are shaped so that handling stays a short, ordered funnel rather than a sprawl.
The rule is short, and every interesting consequence follows from it: **Python runs the body of the first `except` clause, in source order, whose type matches the raised exception.** ## What "matches" means A clause `except E:` matches when the raised exception's class is `E` or any subclass of `E`. It is a class relationship, not an instance comparison, so the whole builtin hierarchy is available to you: `except OSError:` catches `FileNotFoundError`, `PermissionError` and the rest of the OS error family, and `except Exception:` catches essentially everything an application raises. The test is against the *class of the raised instance*. Note that `raise ValueError` with no call still produces an instance — Python instantiates the class for you — so there is never a class-versus-instance distinction to worry about at match time. ## First match, not best match This is the part interviewers probe. Python does not search for the most specific handler; it takes the first one that matches: ```python try: raise ValueError("boom") except Exception: print("broad clause won") # this runs except ValueError: print("never reached") ``` The second clause is unreachable. Python does not warn about it — the clause is perfectly valid code, and the interpreter has no obligation to prove it can run. A static analysis tool will usually flag it, which is one small reason to run one. The practical rule is to order clauses from specific to general, top to bottom, and to remember that the ordering is *your* responsibility, not the language's. Only one clause ever runs. There is no fall-through and no way for a handler to say "and also try the next one" — if you want the enclosing code to see the exception too, you re-raise it. ## Tuple clauses When several types deserve identical handling, one clause can name a parenthesized tuple: ```python try: value = parse(raw) except (ValueError, TypeError) as err: value = fallback(err) ``` The clause matches if the raised exception matches *any* member, and members are themselves matched by subclass, so a tuple can mix families freely. The parentheses are what make it a tuple expression; a list will not do. `except [ValueError, TypeError]:` compiles fine and then fails at match time with `TypeError: catching classes that do not inherit from BaseException is not allowed`, because Python checks the object it was handed, and a list is not an exception class. The same error appears if any member of the tuple is not a `BaseException` subclass — `except (ValueError, int):` fails even when the raised exception *is* a `ValueError`, because the whole clause is validated when the match runs. And note *when* that happens: these are runtime errors raised while handling another exception, so a typo in a rarely-taken handler can sit undetected for a long time. ## The `as` binding on a tuple clause A tuple clause takes an `as` target like any other, and the target is bound to the actual instance, so you can branch on its concrete type inside the body if the shared handling is only partly shared. The binding is deleted when the handler exits, so copy it out if you need it later. ## Practical ordering patterns A handler chain usually reads as a funnel: the narrowest, most expected failure first, then the family it belongs to, then a broad safety net if the code genuinely has one. Two habits keep the chain honest: 1. **Write the specific clauses first.** If you cannot say why a clause is above another, you probably do not need both. 2. **Group by handling, not by taxonomy.** Two exception types that produce the same recovery belong in one tuple clause; two that produce different recovery belong in separate clauses even if they share a base class. ## What a strong answer sounds like "Clauses are checked top to bottom; the first whose class matches the raised exception's class or one of its bases wins, and the rest are skipped. That is why a broad clause above a narrow one silently makes the narrow one dead. A single clause can name a parenthesized tuple to handle several types the same way, and every member has to be an exception class or the match itself raises a `TypeError`." That is the mechanism. How *broadly* you should be catching in the first place is a separate design conversation, and a good candidate keeps the two apart: this question is about which clause the interpreter picks, not about which clause you should have written.
- What happens if a clause names something that is not an exception class, such as a list of types?Nothing at compile time — the clause is a normal expression. When an exception actually reaches the clause, Python raises `TypeError: catching classes that do not inherit from BaseException is not allowed`, and that error is raised while the original exception is being handled. Because it only fires on the path that reaches the clause, a typo in a rare handler can go unnoticed for a long time.
- Does Python warn you about an except clause that can never be reached?No. The interpreter does no reachability analysis over handler chains, and an unreachable clause is valid code that simply never runs. A static analysis tool will usually report it, which is the main practical defence; otherwise the only protection is the habit of ordering clauses from specific to general.
- If two clauses would both match, can the first one hand the exception to the second?Not within the same statement. Exactly one clause runs and there is no fall-through. If the enclosing code should also see the exception, the handler re-raises it — a bare `raise` sends the same exception on outward — and it is then matched against handlers further up the stack, not against the remaining clauses of this statement.
saying these in an interview costs you the question
- Says Python picks the most specific matching clause
- Thinks every matching except clause runs in turn
- Uses a list of types instead of a tuple
- Believes an unreachable clause is a syntax error
- Assumes a clause only matches the exact class, not subclasses