Q&A
How is try/except used in Python?
Overview
Python uses exceptions for error handling: instead of checking upfront whether an operation will succeed ("Look Before You Leap"), the EAFP style tries the operation and handles failure afterward. try/except is the mechanism for this. Unlike Java's checked exceptions, Python exceptions are unchecked - you choose which to catch.
graph TD
A[try block runs] --> B{Exception raised?}
B -->|no| C[else block runs optional]
B -->|yes, matches except| D[except block handles it]
B -->|yes, no match| E[propagates up the call stack]
C --> F[finally block runs ALWAYS]
D --> F
style F fill:#1e1b4b,color:#fff
Basic Structure
try:
risky_code()
except SomeException as e:
handle(e)
When an exception is raised inside try, Python searches the except clauses top to bottom for a type match and runs the first that matches. If none matches, the exception propagates up.
Catching Specific Exceptions
try:
value = int(input("Enter a number: "))
result = 10 / value
except ValueError:
print("Not a valid integer.")
except ZeroDivisionError:
print("Cannot divide by zero.")
Be specific - catch the narrowest exception type you can handle. This avoids accidentally swallowing unrelated errors.
Accessing the Exception Object
Use as to bind the exception to a name:
try:
with open("data.txt") as f:
content = f.read()
except FileNotFoundError as e:
print(f"File missing: {e}") # message from the exception
print(f"Error code: {e.errno}") # attribute access on the exception
Catching Multiple Exceptions in One Clause
except (TypeError, ValueError) as e:
print(f"Bad input: {e}")
else - Runs Only When No Exception Occurred
try:
result = int("42")
except ValueError:
print("Parse failed")
else:
print(f"Parsed: {result}") # only here if try succeeded
else keeps the "happy path" code separate from exception handling - clearer than putting it inside try where an error from it could accidentally be caught.
finally - Runs Always (cleanup)
conn = None
try:
conn = db.connect()
data = conn.query("SELECT …")
except DatabaseError as e:
log(e)
finally:
if conn:
conn.close() # always runs, even on exception or return
finally is the right place for cleanup (closing files, releasing locks) because it's guaranteed to run whether or not an exception occurred. Modern Python often uses context managers (with) instead, which handle this automatically.
Catching All Exceptions (use sparingly)
except Exception as e: # catches most exceptions (not SystemExit/KeyboardInterrupt)
log(e)
except BaseException as e: # catches everything, including SystemExit - rarely correct
...
A bare except: (no type) catches everything silently - an anti-pattern that hides bugs. Prefer at minimum except Exception.
Raising Exceptions
raise ValueError("Value must be positive")
# Re-raise the current exception:
try:
risky()
except SomeError:
log("error occurred")
raise # re-raises the same exception with the original traceback
Custom Exception Classes
class InsufficientFundsError(Exception):
def __init__(self, amount, balance):
self.amount = amount
self.balance = balance
super().__init__(f"Cannot withdraw {amount}; balance is {balance}")
raise InsufficientFundsError(100, 50)
Python Exception Hierarchy
BaseException is the root; Exception is the base for most user-facing errors. Important built-ins: ValueError, TypeError, KeyError, IndexError, FileNotFoundError (subclass of OSError), ZeroDivisionError, AttributeError, RuntimeError. Catch as close to the specific type as possible.
Why This Matters in Interviews
Python interviews use try/except to test error-handling idioms. Key points: catch specific exceptions (not bare except:), understand the else clause (success path, separate from try), finally for cleanup, re-raising with bare raise, and custom exception classes. The EAFP philosophy - try first, handle failure - versus the look-before-you-leap (LBYL) style of defensive checks is a frequently asked design question. Context managers (with) as the modern alternative to try/finally for resource cleanup also comes up.
Common Pitfalls
- Bare
except:orexcept Exception:too broadly, swallowing unexpected errors. - Catching an exception and doing nothing (silently hiding bugs).
- Putting too much code in
try, catching errors you didn't intend. - Using
try/exceptto control normal flow when a simpleifwould be clearer. - Forgetting
raiseto re-raise, accidentally swallowing an exception after logging.
References
- Python exceptions docs: https://docs.python.org/3/tutorial/errors.html
- Python built-in exceptions: https://docs.python.org/3/library/exceptions.html