Exceptions, and the with statement that always cleans up
try/except/else/finally in the order they actually run, and what a context manager guarantees.
After this lesson you can
- Say what each of try, except, else and finally is for
- Catch a specific exception type rather than a bare except
- Explain what a with statement guarantees that a plain try does not
try has four parts, and each one has a distinct job:
try:
value = risky()
except ValueError as e:
print(f"bad value: {e}")
except (TypeError, KeyError) as e:
print(f"bad shape: {e}")
else:
print("no exception, value is ready")
finally:
print("always runs, exception or not")
except only catches what actually raised — code in else is not
guarded, which is exactly the point: else is where you put code that
should run only when nothing went wrong, and a bug inside it is not
quietly swallowed by the except above it. finally runs no matter
what: on success, on a caught exception, on an exception that was not
caught, even after a return inside try.
A bare except: catches everything, including KeyboardInterrupt and
SystemExit — the two exceptions a program almost always wants to
actually stop for. Catch the specific type you expect and know how to
handle; let everything else propagate.
Try it
def attempt(value): steps = [] try: if value < 0: raise ValueError("negative") steps.append("try") except ValueError: steps.append("except") else: steps.append("else") finally: steps.append("finally") return steps def run(): return {"ok": attempt(5), "bad": attempt(-1)}Raising your own exceptions
class InsufficientFundsError(Exception):
def __init__(self, needed, available):
super().__init__(f"needed {needed}, had {available}")
self.needed = needed
self.available = available
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(amount, balance)
return balance - amount
A custom exception class is a real class, usually with almost no body —
its job is to give the except block something specific to catch, and
to carry whatever data the caller will need to react.
EAFP over LBYL
Python's idiom is "easier to ask forgiveness than permission": try the operation, catch the failure, rather than checking every precondition first.
# LBYL — look before you leap
if key in d:
value = d[key]
else:
value = None
# EAFP — the Python way
try:
value = d[key]
except KeyError:
value = None
EAFP wins for two reasons: the check-then-use version can still race
(another thread removes key between the check and the use), and it
looks the key up twice where EAFP looks once.
with — cleanup that cannot be skipped
with open("data.txt") as f:
contents = f.read()
# the block has ended, so f is closed, and it would be even if read() raised
A with block calls the context manager's cleanup (__exit__) whether
the block finished normally, returned early, or raised — the same
guarantee finally gives a try, but attached to the object that owns
the resource instead of hand-written at every call site. open,
database connections, and locks all implement this, which is why
is the default reach for any of them. The context-managers lesson later
in the course shows how to write your own.
def parse_age(text): try: return int(txet) # typo: NameError, not a bad input except Exception: return -1 def finally_returns(): try: raise ValueError("lost") finally: return "finally won" def run(): return {"parse_age_42": parse_age("42"), "finally_returns": finally_returns()}What interviewers ask
- In what order do
try,except,elseandfinallyrun, and why doeselseexist?elseruns only whentryraised nothing, and its own errors are not caught by theexceptclauses above it. - Why is a bare
except:a bad idea? It also catchesKeyboardInterruptandSystemExit, and it hides bugs; catch the narrowest type you can actually handle. - EAFP or LBYL? Python favours trying and catching the specific failure; a check-then-act can race and duplicates the work.
Exercise · Pro
Read a nested setting, and say exactly what went wrong
Exercises are free in the first 3 lessons of every course. From here on they are part of Pro and Sprint; the lesson and its runnable examples stay open.
Upgrade