Python Exception Handling
Python Exception Handling Interview with follow-up questions
1. Can you explain what exception handling is in Python?
Exception handling is the mechanism for dealing with runtime errors without crashing the program. When something goes wrong, Python raises an exception that propagates up the call stack until a matching handler catches it — otherwise the program terminates with a traceback.
The full structure has four clauses:
try:
data = parse(payload) # code that may fail
except ValueError as exc: # handle a specific type
log.warning("bad payload: %s", exc)
else:
process(data) # runs only if no exception
finally:
cleanup() # always runs (cleanup)
What interviewers expect you to get right:
- Catch specific exceptions, not a bare
except:. Bare/except Exceptionswallows bugs and evenKeyboardInterrupt(bare does). Be precise. elseruns when thetrysucceeds;finallyalways runs, even onreturnor re-raise — ideal for releasing resources.- EAFP over LBYL — Python style favors "try it and handle failure" over checking conditions first.
- 3.11+ exception groups let you raise/handle multiple errors at once via
ExceptionGroupandexcept*— relevant for concurrent code. - Use
contextlib.suppress(FileNotFoundError)when you genuinely want to ignore one specific error.
Follow-up 1
What is the difference between an error and an exception?
In Python, an error is a condition that causes the program to terminate abruptly and cannot be handled by the program itself. Examples of errors include syntax errors and logical errors. On the other hand, an exception is an event that occurs during the execution of a program that disrupts the normal flow of the program, but can be handled by the program itself. Exceptions are typically caused by external factors or unexpected conditions, such as invalid user input or network failures.
Follow-up 2
Can you give an example of an exception?
Sure! One example of an exception is the ZeroDivisionError exception. This exception occurs when you try to divide a number by zero, which is mathematically undefined. Here's an example code snippet that raises a ZeroDivisionError exception:
x = 10
y = 0
result = x / y
Follow-up 3
What are some common exceptions in Python?
There are many built-in exceptions in Python, some of the common ones include:
ZeroDivisionError: Raised when division or modulo operation is performed with zero as the divisor.TypeError: Raised when an operation or function is applied to an object of an inappropriate type.ValueError: Raised when a function receives an argument of the correct type but an inappropriate value.FileNotFoundError: Raised when a file or directory is requested but cannot be found.KeyError: Raised when a dictionary key is not found.IndexError: Raised when a sequence subscript is out of range.NameError: Raised when a local or global name is not found.IOError: Raised when an input/output operation fails.ImportError: Raised when an import statement fails to find the module definition.AttributeError: Raised when an attribute reference or assignment fails.
These are just a few examples, there are many more exceptions available in Python.
Follow-up 4
How can we handle multiple exceptions in Python?
In Python, we can handle multiple exceptions using multiple except blocks or by using a single except block with multiple exception types. Here's an example that demonstrates both approaches:
try:
# Code that may raise exceptions
pass
except ValueError:
# Handle ValueError
pass
except ZeroDivisionError:
# Handle ZeroDivisionError
pass
except (TypeError, IndexError):
# Handle TypeError and IndexError
pass
2. What is the purpose of the 'try' and 'except' blocks in Python?
try/except lets you run code that might fail and handle the failure gracefully instead of crashing. Code that may raise goes in try; the recovery logic goes in except, matched by exception type.
try:
value = int(user_input)
except ValueError:
print("Please enter a number")
Points interviewers look for:
- Catch the specific type, not a bare
except:. Bareexcepthides bugs and even catchesKeyboardInterrupt/SystemExit. - Multiple handlers, most specific first; or group types in a tuple:
try:
risky()
except (KeyError, IndexError) as exc: # one handler, several types
log.error("lookup failed: %s", exc)
except Exception as exc: # broad fallback, last
log.exception("unexpected")
as excbinds the exception object so you can inspect or log it.- Pair with
else(runs on success) andfinally(always runs, for cleanup). - Don't use exceptions for normal control flow in hot loops where a cheap check suffices — though Python's EAFP style does prefer try/except over pre-checking in most cases.
Follow-up 1
Can you give an example of how to use 'try' and 'except'?
Sure! Here's an example:
try:
num = int(input('Enter a number: '))
result = 10 / num
print('The result is:', result)
except ZeroDivisionError:
print('Error: Cannot divide by zero')
except ValueError:
print('Error: Invalid input')
Follow-up 2
What happens if an exception is not caught in the 'except' block?
If an exception is not caught in the 'except' block, it will propagate up the call stack until it is caught by an outer 'try' block or until it reaches the top level of the program. If the exception is not caught at all, the program will terminate and an error message will be displayed.
Follow-up 3
Can we have multiple 'except' blocks for a single 'try' block?
Yes, we can have multiple 'except' blocks for a single 'try' block. Each 'except' block can handle a specific type of exception. If an exception occurs, Python will check each 'except' block in order and execute the code in the first 'except' block that matches the type of the exception. If none of the 'except' blocks match, the exception will propagate up the call stack.
3. What is the 'finally' keyword in Python and when is it used?
finally defines a block that always runs when leaving a try statement — whether it completed normally, raised an exception, or exited early via return/break/continue. It's the place for cleanup that must happen no matter what: closing files, releasing locks, rolling back transactions.
def read_config(path: str) -> str:
f = open(path, encoding="utf-8")
try:
return f.read() # even this return runs finally first
finally:
f.close() # guaranteed cleanup
Things interviewers like to probe:
finallyruns even after areturnin thetryorexceptblock — and iffinallyitself returns, it overrides that earlier return (a classic gotcha; avoid returning fromfinally).- For resource cleanup, a
withstatement (context manager) is usually cleaner than manualtry/finally—with open(...) as f:handlesclose()for you. finallydiffers fromelse:elseruns only when no exception occurred;finallyruns unconditionally.- If an exception is in flight and
finallydoesn't re-raise or return, the original exception continues to propagate afterfinallycompletes.
Follow-up 1
Can you give an example of how to use 'finally'?
Sure! Here's an example:
try:
# Some code that may raise an exception
...
except SomeException:
# Exception handling code
...
finally:
# Code that will always be executed
# This could be closing a file, releasing resources, etc.
...
In this example, the code inside the 'finally' block will always be executed, regardless of whether an exception is raised or not.
Follow-up 2
What is the difference between 'else' and 'finally' in exception handling?
In exception handling, the 'else' block is executed only if no exception is raised in the try block, whereas the 'finally' block is always executed, regardless of whether an exception is raised or not. The 'else' block is used to specify code that should be executed if the try block completes successfully, without any exceptions being raised. On the other hand, the 'finally' block is used to specify code that should be executed regardless of the outcome of the try block, whether an exception is raised or not.
Follow-up 3
Does the 'finally' block always execute?
Yes, the 'finally' block always executes, regardless of whether an exception is raised or not. Even if an exception is raised and caught in the except block, the code inside the 'finally' block will still be executed. This makes the 'finally' block useful for performing cleanup operations or releasing resources that need to be done regardless of the outcome of the try-except block.
4. How can we raise our own exceptions in Python?
You raise an exception with the raise statement, passing an exception instance (or class) with an optional message:
raise ValueError("Invalid input")
This stops normal execution and propagates up the call stack until a handler catches it.
For domain errors, define custom exceptions by subclassing Exception (never BaseException directly):
class InsufficientFundsError(Exception):
def __init__(self, requested: float, available: float) -> None:
super().__init__(f"need {requested}, have {available}")
self.requested = requested
self.available = available
raise InsufficientFundsError(100, 30)
What interviewers want to hear:
- Raise specific, meaningful types — a custom exception lets callers catch exactly your error without swallowing unrelated ones.
- Exception chaining with
raise ... frompreserves context when re-raising:
try:
data = json.loads(raw)
except json.JSONDecodeError as exc:
raise ConfigError("bad config file") from exc
Use from None to deliberately suppress the original cause.
- Bare
raiseinside anexceptre-raises the current exception, keeping its original traceback. - Subclass a shared base (e.g.
class AppError(Exception)) so callers can catch a whole family at once.
Follow-up 1
What is the purpose of raising exceptions?
The purpose of raising exceptions is to handle exceptional situations in our code. Exceptions allow us to indicate that something unexpected or erroneous has occurred during the execution of our program.
By raising exceptions, we can interrupt the normal flow of execution and transfer control to an exception handler. This allows us to gracefully handle errors, provide error messages, and take appropriate actions to recover from the exceptional situation.
Follow-up 2
Can you give an example of a custom exception?
Yes, we can define our own custom exceptions by creating a new class that inherits from the built-in Exception class or any of its subclasses.
Here is an example of a custom exception named CustomException:
class CustomException(Exception):
pass
We can then raise this custom exception using the raise statement, just like any other exception.
raise CustomException('This is a custom exception')
By defining custom exceptions, we can create more specific and meaningful exceptions that are tailored to our application's needs.
Follow-up 3
How can we pass arguments to a custom exception?
To pass arguments to a custom exception, we can define an __init__ method in our custom exception class. This method will be called when the exception is raised, allowing us to initialize the exception object with the desired arguments.
Here is an example of a custom exception named CustomException that accepts an argument:
class CustomException(Exception):
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
# Raise the custom exception with arguments
raise CustomException('Argument 1', 'Argument 2')
In this example, the CustomException class has an __init__ method that takes two arguments (arg1 and arg2). When the exception is raised, we pass the desired values for these arguments, which can then be accessed within the exception handler.
5. What is the use of the 'assert' statement in Python?
assert checks that a condition you believe is always true actually holds, raising AssertionError (with an optional message) if it doesn't. It's a tool for catching programmer errors and documenting invariants during development.
def average(values: list[float]) -> float:
assert values, "values must not be empty"
return sum(values) / len(values)
The gotcha interviewers almost always test: asserts are stripped when Python runs with -O (optimized mode). So:
- Never use
assertfor runtime validation of user input, API arguments, or security checks — in optimized builds the check vanishes and the bad path runs. Use an explicitif ... raise ValueError(...)for that. - Use
assertfor internal invariants, sanity checks, and tests (pytest builds its rich introspection on theassertstatement). assert (cond, "msg")with a tuple is always truthy — a common bug. The correct form isassert cond, "msg"(no parentheses around both).
In short: assert documents "this should never happen" assumptions; raise real exceptions for "this might happen" conditions.
Follow-up 1
Can you give an example of how to use 'assert'?
Certainly! Here's an example:
x = 5
assert x > 0, 'x should be greater than 0'
print('x is positive')
Follow-up 2
What happens when an 'assert' statement fails?
When an 'assert' statement fails, it raises an AssertionError. This exception can be caught and handled like any other exception in Python.
Follow-up 3
In what scenarios is it beneficial to use 'assert' over traditional exception handling?
The 'assert' statement is particularly useful during development and testing phases. It helps to catch logical errors and assumptions that should always be true. By using 'assert', you can quickly identify and fix issues in your code. However, it is important to note that 'assert' statements should not be used for error handling in production code, as they can be disabled with the -O or -OO command line switches.
Live mock interview
Mock interview: Python Exception Handling
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.