Practice Questions — Exception Handling in Python
← Back to NotesTopic-Specific Questions
Question 1
Easy
What is the output of the following code?
try:
print("A")
print("B")
except:
print("C")
print("D")If no exception occurs in try, the except block is skipped.
ABDQuestion 2
Easy
What is the output?
try:
print("A")
x = 1 / 0
print("B")
except ZeroDivisionError:
print("C")
print("D")After an exception, the rest of the try block is skipped.
ACDQuestion 3
Easy
What is the output?
try:
x = int("hello")
except ValueError:
print("Error caught")
print("Done")int('hello') raises a ValueError.
Error caughtDoneQuestion 4
Easy
What is the output?
try:
x = int("42")
except ValueError:
print("Error")
else:
print(f"Value: {x}")The else block runs when no exception occurs.
Value: 42Question 5
Easy
What is the output?
try:
print("start")
except:
print("error")
finally:
print("done")finally always runs, even when there is no exception.
startdoneQuestion 6
Medium
What is the output?
try:
print("A")
x = 1 / 0
except ZeroDivisionError:
print("B")
else:
print("C")
finally:
print("D")else runs only on success. finally always runs.
ABDQuestion 7
Medium
What is the output?
try:
x = int("10")
y = x / 2
except ValueError:
print("A")
except ZeroDivisionError:
print("B")
else:
print("C")
finally:
print("D")No exception occurs. Which blocks run?
CDQuestion 8
Medium
What is the output?
def f():
try:
return 1
finally:
print("finally")
result = f()
print(result)finally runs even after a return statement.
finally1Question 9
Medium
What is the output?
try:
numbers = [1, 2, 3]
print(numbers[5])
except IndexError as e:
print(type(e).__name__)
print(e)as e captures the exception object.
IndexErrorlist index out of rangeQuestion 10
Medium
What is the output?
def test():
try:
return "try"
except:
return "except"
finally:
return "finally"
print(test())If finally has a return, it overrides the return from try or except.
finallyQuestion 11
Medium
What is the output?
try:
d = {"a": 1, "b": 2}
print(d["c"])
except KeyError as e:
print(f"Key {e} not found")
except Exception as e:
print(f"General: {e}")KeyError is checked before Exception. Only the first matching except runs.
Key 'c' not foundQuestion 12
Hard
What is the output?
def process(x):
try:
if x < 0:
raise ValueError("negative")
return x * 2
except ValueError as e:
print(f"Caught: {e}")
return -1
finally:
print("cleanup")
print(process(5))
print(process(-3))Trace both calls. finally runs before the return value is delivered.
cleanup10Caught: negativecleanup-1Question 13
Hard
What is the output?
try:
try:
x = 1 / 0
except ValueError:
print("inner ValueError")
finally:
print("inner finally")
except ZeroDivisionError:
print("outer ZeroDivision")
finally:
print("outer finally")The inner except does not match. The exception propagates to the outer try.
inner finallyouter ZeroDivisionouter finallyQuestion 14
Hard
What is the output?
class MyError(Exception):
pass
def f():
raise MyError("custom error")
try:
f()
except MyError as e:
print(type(e).__name__)
print(e)
except Exception:
print("general")MyError is a subclass of Exception. It is checked before Exception.
MyErrorcustom errorQuestion 15
Hard
What is the output?
def f(n):
try:
if n == 0:
raise ValueError("zero")
if n == 1:
raise TypeError("one")
return n
except ValueError:
return "V"
except TypeError:
return "T"
print(f(0), f(1), f(2))Each call is independent. Follow the flow for each value of n.
V T 2Question 16
Hard
What is the output?
result = []
try:
result.append("try")
x = int("abc")
result.append("after int")
except ValueError:
result.append("except")
else:
result.append("else")
finally:
result.append("finally")
print(result)Track which append() calls execute.
['try', 'except', 'finally']Question 17
Medium
What is the difference between
except Exception and except: (bare except)?Think about the exception hierarchy. What does BaseException include?
except Exception catches all regular exceptions (ValueError, TypeError, etc.) but not KeyboardInterrupt, SystemExit, or GeneratorExit. except: (bare except) catches everything, including KeyboardInterrupt and SystemExit, which can make the program hard to stop with Ctrl+C.Question 18
Hard
Why should you put specific exception handlers before general ones?
Python checks except blocks from top to bottom.
Python checks except blocks from top to bottom and enters the first one that matches. Since a parent class matches all its children, putting
except Exception before except ValueError means ValueError is always caught by Exception first, and the ValueError handler never runs. Specific handlers must come first to get a chance to match.Mixed & Application Questions
Question 1
Easy
What is the output?
x = "10"
try:
y = int(x)
except ValueError:
y = 0
print(y + 5)"10" is a valid integer string.
15Question 2
Easy
What is the output?
x = "abc"
try:
y = int(x)
except ValueError:
y = 0
print(y + 5)"abc" cannot be converted to int.
5Question 3
Medium
What is the output?
def safe_get(lst, index, default=None):
try:
return lst[index]
except IndexError:
return default
print(safe_get([10, 20, 30], 1))
print(safe_get([10, 20, 30], 5))
print(safe_get([10, 20, 30], 5, -1))Index 5 is out of range for a 3-element list.
20None-1Question 4
Medium
What is the output?
def convert(values):
result = []
for v in values:
try:
result.append(int(v))
except ValueError:
result.append(None)
return result
print(convert(["1", "abc", "3", "xyz", "5"]))Each string is converted independently. Invalid ones become None.
[1, None, 3, None, 5]Question 5
Medium
What is the output?
def divide_all(numbers, divisor):
results = []
for n in numbers:
try:
results.append(n / divisor)
except ZeroDivisionError:
results.append("inf")
except TypeError:
results.append("err")
return results
print(divide_all([10, 20, 30], 5))
print(divide_all([10, 20, 30], 0))Each division is in its own try/except, so one error does not stop others.
[2.0, 4.0, 6.0]['inf', 'inf', 'inf']Question 6
Medium
What is the output?
data = {"a": 1, "b": 2}
keys = ["a", "c", "b", "d"]
result = []
for key in keys:
try:
result.append(data[key])
except KeyError:
result.append(0)
print(result)Keys 'c' and 'd' do not exist in the dictionary.
[1, 0, 2, 0]Question 7
Hard
What is the output?
def f(x):
try:
result = 100 / x
except ZeroDivisionError:
return "zero"
except TypeError:
return "type"
else:
if result > 50:
raise ValueError("too big")
return result
try:
print(f(2))
print(f(0))
print(f(1))
except ValueError as e:
print(e)f(1) gives result=100, which is >50, so ValueError is raised.
50.0zerotoo bigQuestion 8
Hard
What is the output?
class TooYoungError(Exception):
pass
class TooOldError(Exception):
pass
def check_age(age):
if age < 13:
raise TooYoungError(f"{age} is too young")
if age > 65:
raise TooOldError(f"{age} is too old")
return "OK"
for age in [10, 25, 70]:
try:
result = check_age(age)
print(f"{age}: {result}")
except TooYoungError as e:
print(f"{age}: Young - {e}")
except TooOldError as e:
print(f"{age}: Old - {e}")Each age triggers a different path: too young, OK, or too old.
10: Young - 10 is too young25: OK70: Old - 70 is too oldQuestion 9
Hard
What is the output?
def nested():
try:
try:
raise ValueError("inner")
except TypeError:
print("caught type")
finally:
print("inner finally")
except ValueError:
print("caught value")
finally:
print("outer finally")
nested()The inner except does not match ValueError. The exception propagates outward.
inner finallycaught valueouter finallyQuestion 10
Hard
What is the output?
def risky(n):
if n == 0:
raise ValueError("zero")
if n == 1:
raise TypeError("one")
return n * 10
results = []
for i in range(4):
try:
results.append(risky(i))
except (ValueError, TypeError) as e:
results.append(str(e))
print(results)Multiple exceptions in one except clause using a tuple.
['zero', 'one', 20, 30]Question 11
Medium
What is the EAFP principle in Python? How does it differ from LBYL?
EAFP: try first, handle errors. LBYL: check first, then act.
EAFP (Easier to Ask Forgiveness than Permission) means trying an operation and handling the exception if it fails. LBYL (Look Before You Leap) means checking if the operation will succeed before attempting it. Python favors EAFP because it is often faster (avoids double lookups), handles race conditions better, and is considered more Pythonic.
Multiple Choice Questions
MCQ 1
What keyword starts an exception handling block in Python?
Answer: B
B is correct. Python uses
B is correct. Python uses
try to begin an exception handling block. catch is used in Java and JavaScript, not Python.MCQ 2
What happens if an exception occurs in a try block and no matching except handler is found?
Answer: C
C is correct. If no matching except is found, the exception propagates up the call stack. If no handler is found anywhere, the program crashes with a traceback.
C is correct. If no matching except is found, the exception propagates up the call stack. If no handler is found anywhere, the program crashes with a traceback.
MCQ 3
Which exception is raised by int('abc')?
Answer: B
B is correct.
B is correct.
int('abc') raises ValueError because 'abc' is a string (correct type) but cannot be converted to an integer (invalid value). TypeError would be raised if the argument type itself was wrong.MCQ 4
When does the else clause in a try/except/else block execute?
Answer: C
C is correct. The
C is correct. The
else clause runs only when the try block completes without raising any exception. If any exception occurs (whether caught or not), else is skipped.MCQ 5
When does the finally clause execute?
Answer: D
D is correct.
D is correct.
finally always runs: after try succeeds, after except handles an error, and even after return statements. It is guaranteed to execute.MCQ 6
What does 'except ValueError as e' do?
Answer: B
B is correct. The
B is correct. The
as e syntax captures the exception object in the variable e. You can then access the error message with str(e) and the exception type with type(e).MCQ 7
Which of these is NOT a built-in Python exception?
Answer: B
B is correct.
B is correct.
NullPointerError is a Java exception, not Python. Python does not have null pointers. The equivalent situation in Python would raise AttributeError or TypeError.MCQ 8
What is the correct order of clauses in a try statement?
Answer: C
C is correct. The required order is: try, then except (one or more), then else (optional), then finally (optional). Python raises a SyntaxError if this order is violated.
C is correct. The required order is: try, then except (one or more), then else (optional), then finally (optional). Python raises a SyntaxError if this order is violated.
MCQ 9
What does raise do?
Answer: B
B is correct.
B is correct.
raise creates and throws (raises) an exception. The exception propagates up the call stack until it is caught by an except block or crashes the program. It does not catch, print, or terminate by itself.MCQ 10
How do you create a custom exception class?
Answer: B
B is correct. Custom exceptions are classes that inherit from
B is correct. Custom exceptions are classes that inherit from
Exception (or one of its subclasses). Option A is wrong because Error is not a Python class. Options C and D are not valid Python syntax for defining exceptions.MCQ 11
Can you have multiple except blocks for one try block?
Answer: C
C is correct. A single try block can have multiple except blocks, each handling a different exception type. Python checks them top to bottom and enters the first match.
C is correct. A single try block can have multiple except blocks, each handling a different exception type. Python checks them top to bottom and enters the first match.
MCQ 12
What happens if a finally block contains a return statement?
Answer: C
C is correct. If finally contains a return, it overrides any return from try or except. This is because finally always runs last, and its return replaces the pending return value. This behavior is confusing, which is why returning from finally is strongly discouraged.
C is correct. If finally contains a return, it overrides any return from try or except. This is because finally always runs last, and its return replaces the pending return value. This behavior is confusing, which is why returning from finally is strongly discouraged.
MCQ 13
What exception is raised when you access a key that does not exist in a dictionary?
Answer: C
C is correct. Accessing a missing dictionary key raises
C is correct. Accessing a missing dictionary key raises
KeyError. Option B (IndexError) is for lists/sequences. Option D (LookupError) is the parent class of both KeyError and IndexError, but the specific exception raised is KeyError.MCQ 14
What does EAFP stand for in Python philosophy?
Answer: B
B is correct. EAFP means try the operation and handle the exception if it fails, rather than checking conditions in advance. This is the preferred Python style, as opposed to LBYL (Look Before You Leap).
B is correct. EAFP means try the operation and handle the exception if it fails, rather than checking conditions in advance. This is the preferred Python style, as opposed to LBYL (Look Before You Leap).
MCQ 15
Which of these exceptions is NOT a subclass of Exception?
Answer: C
C is correct.
C is correct.
KeyboardInterrupt inherits from BaseException, not Exception. This means except Exception does not catch it, which allows users to always interrupt a program with Ctrl+C.MCQ 16
Can an except block catch multiple exception types at once?
Answer: B
B is correct. You catch multiple types with a tuple:
B is correct. You catch multiple types with a tuple:
except (ValueError, TypeError). Option D was valid in Python 2 but not in Python 3. Option C does not work (it evaluates the or expression).MCQ 17
What exception does 10 / 0 raise?
Answer: C
C is correct. Division by zero raises
C is correct. Division by zero raises
ZeroDivisionError. Option D (ArithmeticError) is its parent class. While except ArithmeticError would also catch it, the specific exception raised is ZeroDivisionError.MCQ 18
What is the difference between SyntaxError and an exception?
Answer: B
B is correct. SyntaxError is detected during parsing (before any code runs). Exceptions like ValueError, TypeError occur during execution. Technically, SyntaxError can be caught with try/except in some cases (e.g., in eval()), but in normal code, it prevents execution entirely.
B is correct. SyntaxError is detected during parsing (before any code runs). Exceptions like ValueError, TypeError occur during execution. Technically, SyntaxError can be caught with try/except in some cases (e.g., in eval()), but in normal code, it prevents execution entirely.
MCQ 19
If a try block has both else and finally, and no exception occurs, what is the execution order?
Answer: B
B is correct. When no exception occurs: try runs first, then else (success path), then finally (always runs). The except blocks are skipped entirely.
B is correct. When no exception occurs: try runs first, then else (success path), then finally (always runs). The except blocks are skipped entirely.
MCQ 20
What is the purpose of 'raise' without any argument inside an except block?
Answer: B
B is correct. A bare
B is correct. A bare
raise inside an except block re-raises the current exception. This is useful when you want to log the error or do partial handling and then let it propagate further up the call stack.Coding Challenges
Challenge 1: Safe Calculator
EasyWrite a function safe_calculate(a, op, b) where op is one of '+', '-', '*', '/'. Handle ZeroDivisionError for division by zero and ValueError for unknown operators. Return the result or an error message string.
Sample Input
(No input required)
Sample Output
10 + 5 = 15
10 / 0 = Error: division by zero
10 % 3 = Error: unknown operator '%'
Use try/except. Return numeric result on success, string message on error.
def safe_calculate(a, op, b):
try:
if op == '+':
return a + b
elif op == '-':
return a - b
elif op == '*':
return a * b
elif op == '/':
return a / b
else:
raise ValueError(f"unknown operator '{op}'")
except ZeroDivisionError:
return "Error: division by zero"
except ValueError as e:
return f"Error: {e}"
test_cases = [(10, '+', 5), (10, '-', 3), (10, '*', 4), (10, '/', 2), (10, '/', 0), (10, '%', 3)]
for a, op, b in test_cases:
print(f"{a} {op} {b} = {safe_calculate(a, op, b)}")Challenge 2: Robust Type Converter
EasyWrite a function convert(value, target_type) that converts a value to the target type (int, float, or str). Use try/except to catch conversion errors and return a tuple (success: bool, result).
Sample Input
(No input required)
Sample Output
(True, 42)
(False, "Cannot convert 'hello' to int")
(True, 3.14)
(True, '100')
Use try/except. Return a 2-tuple: (True, converted_value) or (False, error_message).
def convert(value, target_type):
try:
result = target_type(value)
return (True, result)
except (ValueError, TypeError) as e:
return (False, f"Cannot convert {value!r} to {target_type.__name__}")
print(convert("42", int))
print(convert("hello", int))
print(convert("3.14", float))
print(convert(100, str))Challenge 3: Safe List Operations
EasyWrite three functions: safe_get(lst, index) that returns the element at index or None if out of range, safe_pop(lst, index) that removes and returns the element or None, and safe_divide_list(lst) that returns each element divided by the next (handle division by zero).
Sample Input
(No input required)
Sample Output
safe_get([1,2,3], 1) = 2
safe_get([1,2,3], 5) = None
safe_pop result: 2, list: [1, 3]
safe_divide: [0.5, 0.67, inf]
Use try/except in each function.
def safe_get(lst, index):
try:
return lst[index]
except IndexError:
return None
def safe_pop(lst, index):
try:
return lst.pop(index)
except IndexError:
return None
def safe_divide_list(lst):
results = []
for i in range(len(lst) - 1):
try:
results.append(round(lst[i] / lst[i+1], 2))
except ZeroDivisionError:
results.append(float('inf'))
return results
print(f"safe_get([1,2,3], 1) = {safe_get([1,2,3], 1)}")
print(f"safe_get([1,2,3], 5) = {safe_get([1,2,3], 5)}")
lst = [1, 2, 3]
result = safe_pop(lst, 1)
print(f"safe_pop result: {result}, list: {lst}")
print(f"safe_divide: {safe_divide_list([1, 2, 3, 0, 5])}")Challenge 4: File Reader with Error Handling
MediumWrite a function read_numbers(filename) that reads a file containing one number per line, converts each to float, and returns the list. Handle FileNotFoundError and ValueError (for invalid lines). Skip invalid lines but count them. Return a dict with 'numbers', 'invalid_count', and 'error' keys.
Sample Input
(No input required)
Sample Output
{'numbers': [10.0, 20.0, 30.0], 'invalid_count': 2, 'error': None}
Handle FileNotFoundError and ValueError. Skip invalid lines.
def read_numbers(filename):
result = {'numbers': [], 'invalid_count': 0, 'error': None}
try:
with open(filename, 'r') as f:
for line in f:
try:
result['numbers'].append(float(line.strip()))
except ValueError:
result['invalid_count'] += 1
except FileNotFoundError:
result['error'] = f"File '{filename}' not found"
return result
# Create test file
with open('numbers.txt', 'w') as f:
f.write('10\n20\nabc\n30\nxyz\n')
print(read_numbers('numbers.txt'))
print(read_numbers('missing.txt'))Challenge 5: Custom Exception: Password Validator
MediumCreate custom exceptions: PasswordTooShortError, PasswordNoDigitError, PasswordNoUpperError. Write validate_password(password) that raises the appropriate exception. Test with various passwords.
Sample Input
(No input required)
Sample Output
MyP@ss1 -> Valid
abc -> PasswordTooShortError: minimum 6 characters, got 3
abcdef -> PasswordNoDigitError: must contain at least one digit
abcde1 -> PasswordNoUpperError: must contain at least one uppercase letter
Each exception class should carry specific details about the failure.
class PasswordTooShortError(Exception):
def __init__(self, length):
super().__init__(f"minimum 6 characters, got {length}")
class PasswordNoDigitError(Exception):
def __init__(self):
super().__init__("must contain at least one digit")
class PasswordNoUpperError(Exception):
def __init__(self):
super().__init__("must contain at least one uppercase letter")
def validate_password(password):
if len(password) < 6:
raise PasswordTooShortError(len(password))
if not any(c.isdigit() for c in password):
raise PasswordNoDigitError()
if not any(c.isupper() for c in password):
raise PasswordNoUpperError()
return True
test_passwords = ["MyP@ss1", "abc", "abcdef", "abcde1"]
for pwd in test_passwords:
try:
validate_password(pwd)
print(f"{pwd} -> Valid")
except (PasswordTooShortError, PasswordNoDigitError, PasswordNoUpperError) as e:
print(f"{pwd} -> {type(e).__name__}: {e}")Challenge 6: Retry Decorator Pattern
MediumWrite a function retry(func, max_attempts, *args) that calls func(*args) up to max_attempts times. If func raises an exception, catch it and try again. Return the result on success, or raise the last exception after all attempts fail.
Sample Input
(No input required)
Sample Output
Attempt 1: failed
Attempt 2: failed
Attempt 3: success! Result: 42
Attempt 1: failed
Attempt 2: failed
Attempt 3: failed
All 3 attempts failed: simulated failure
Track attempt numbers. Re-raise the last exception if all attempts fail.
def retry(func, max_attempts, *args):
last_error = None
for attempt in range(1, max_attempts + 1):
try:
result = func(*args)
print(f"Attempt {attempt}: success! Result: {result}")
return result
except Exception as e:
last_error = e
print(f"Attempt {attempt}: failed")
raise last_error
import random
random.seed(42)
def unreliable():
if random.random() < 0.7:
raise ValueError("simulated failure")
return 42
try:
retry(unreliable, 3)
except ValueError:
pass
print()
def always_fail():
raise ValueError("simulated failure")
try:
retry(always_fail, 3)
except ValueError as e:
print(f"All 3 attempts failed: {e}")Challenge 7: Safe Dictionary Accessor
HardWrite a function deep_get(data, path, default=None) that safely accesses nested dictionary keys using a dot-separated path string. For example, deep_get(data, 'user.address.city') should access data['user']['address']['city']. Handle KeyError, TypeError, and IndexError gracefully.
Sample Input
(No input required)
Sample Output
user.name -> Aarav
user.address.city -> Delhi
user.phone -> None
user.address.zip.code -> None
Use try/except to handle missing keys and type errors at any level.
def deep_get(data, path, default=None):
keys = path.split('.')
current = data
try:
for key in keys:
if isinstance(current, list):
current = current[int(key)]
else:
current = current[key]
return current
except (KeyError, TypeError, IndexError, ValueError):
return default
data = {
'user': {
'name': 'Aarav',
'age': 16,
'address': {
'city': 'Delhi',
'state': 'Delhi'
},
'scores': [85, 92, 78]
}
}
print(f"user.name -> {deep_get(data, 'user.name')}")
print(f"user.address.city -> {deep_get(data, 'user.address.city')}")
print(f"user.phone -> {deep_get(data, 'user.phone')}")
print(f"user.address.zip.code -> {deep_get(data, 'user.address.zip.code')}")
print(f"user.scores.0 -> {deep_get(data, 'user.scores.0')}")Challenge 8: Exception Statistics Collector
HardWrite a function test_expressions(expressions) that takes a list of string expressions, evaluates each with eval(), and returns a dictionary mapping exception type names to their counts, plus a 'success' count. Include the expressions that failed under each exception type.
Sample Input
(No input required)
Sample Output
{'success': 3, 'ZeroDivisionError': {'count': 1, 'expressions': ['1/0']}, 'NameError': {'count': 1, 'expressions': ['x+1']}}
Use eval() to evaluate expressions. Catch Exception as the base and use type(e).__name__ to categorize.
def test_expressions(expressions):
stats = {'success': 0}
for expr in expressions:
try:
eval(expr)
stats['success'] += 1
except Exception as e:
error_type = type(e).__name__
if error_type not in stats:
stats[error_type] = {'count': 0, 'expressions': []}
stats[error_type]['count'] += 1
stats[error_type]['expressions'].append(expr)
return stats
expressions = [
'2 + 3',
'10 / 0',
'int("abc")',
'[1,2][5]',
'x + 1',
'3 * 4',
'{"a":1}["b"]',
'5 ** 2',
]
result = test_expressions(expressions)
for key, value in result.items():
print(f"{key}: {value}")Need to Review the Concepts?
Go back to the detailed notes for this chapter.
Read Chapter NotesWant to learn Python with a live mentor?
Explore our Python course