try / except / else / finally Interview Questions & Answers
5 questions Updated 2026-06-18
Python interview questions on try/except/else/finally, catching multiple exception types, why bare except is bad, finally with return, and exception chaining with raise from.
A full statement has four parts. try holds the code that might fail.
except runs only if a matching exception was raised in try. else
runs only if try finished without raising — it's for the "success path"
code you don't want wrapped in the try. finally runs always, whether
there was an exception or not, and even if you return/break — it's for cleanup.
try:
conn = open_db() # might raise
except DBError as e:
log(e) # runs only on failure
else:
conn.commit() # runs only on success
finally:
conn.close() # always runs — cleanup
Putting the success-only code in else (instead of at the bottom of try) keeps
the try block narrow, so except only catches errors from the risky line.
Group related types in a tuple after except, or use separate except
clauses when each needs different handling. The as e binds the exception
instance, letting you inspect its message, args, or attributes.
try:
value = int(raw)
except (ValueError, TypeError) as e: # tuple -> one handler for both
print(f"bad input: {e}") # e is the exception object
except KeyError: # separate handler, different logic
print("missing key")
Clauses are checked top to bottom, and the first match wins — so order specific exceptions before their base classes. Use the tuple form when several types share handling; use separate clauses when they don't.
A bare except: (or except BaseException:) catches everything — including
KeyboardInterrupt and SystemExit, which are how the user Ctrl-Cs or the
program exits. It also swallows bugs like NameError or TypeError that you'd
rather see crash loudly, making problems invisible.
try:
do_work()
except: # too broad — even Ctrl-C is caught
pass # silent failure — hides real bugs
try:
do_work()
except Exception as e: # catches errors, not Ctrl-C / SystemExit
log.exception(e) # at least record what happened
Catch the narrowest exception you can actually handle. If you must catch
broadly, use except Exception (not bare), and always log rather than silently
pass.
finally always runs, even when try or except already has a return. If
finally itself returns (or raises), it overrides the pending return or
exception — the value from try/except is silently discarded. This is a classic
gotcha.
def f():
try:
return 1 # value queued...
finally:
return 2 # ...but finally's return wins
f() # 2, not 1
def g():
try:
raise ValueError
finally:
return "swallowed" # finally return suppresses the exception!
g() # "swallowed" — exception vanishes
Avoid return/break inside finally: it masks both return values and
exceptions. Keep finally for cleanup only.
A bare raise (no argument) inside an except block re-raises the current
exception, preserving its original traceback — useful for logging then letting it
propagate. To translate one error into another while keeping the cause, use
raise NewError from original, which sets __cause__ and shows both in the
traceback.
try:
parse(config)
except KeyError as e:
log.error("config invalid")
raise # re-raise same exception, original traceback
try:
value = data["port"]
except KeyError as e:
raise ConfigError("missing port") from e # explicit chaining
When an exception is raised inside an except block without from, Python
still links it implicitly via __context__ ("During handling of the above..."). Use
from e to make the cause explicit, or from None to suppress the chain.
Practice tests are coming soon
Get notified when interactive mock interviews and quizzes launch.