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.