Chapter 20 Advanced 58 Questions

Practice Questions — Exception Handling in Python

← Back to Notes
7 Easy
12 Medium
10 Hard

Topic-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.
A
B
D
Question 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.
A
C
D
Question 3
Easy
What is the output?
try:
    x = int("hello")
except ValueError:
    print("Error caught")

print("Done")
int('hello') raises a ValueError.
Error caught
Done
Question 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: 42
Question 5
Easy
What is the output?
try:
    print("start")
except:
    print("error")
finally:
    print("done")
finally always runs, even when there is no exception.
start
done
Question 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.
A
B
D
Question 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?
C
D
Question 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.
finally
1
Question 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.
IndexError
list index out of range
Question 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.
finally
Question 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 found
Question 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.
cleanup
10
Caught: negative
cleanup
-1
Question 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 finally
outer ZeroDivision
outer finally
Question 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.
MyError
custom error
Question 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 2
Question 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.
15
Question 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.
5
Question 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.
20
None
-1
Question 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.0
zero
too big
Question 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 young
25: OK
70: Old - 70 is too old
Question 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 finally
caught value
outer finally
Question 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?
  • A. catch
  • B. try
  • C. handle
  • D. error
Answer: B
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?
  • A. The exception is silently ignored
  • B. The program prints 'Error' and continues
  • C. The exception propagates up to the calling function
  • D. Python automatically fixes the error
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.
MCQ 3
Which exception is raised by int('abc')?
  • A. TypeError
  • B. ValueError
  • C. SyntaxError
  • D. RuntimeError
Answer: B
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?
  • A. Always, after try
  • B. Only if an exception was caught
  • C. Only if no exception occurred in try
  • D. Only if the finally block runs
Answer: C
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?
  • A. Only if an exception occurred
  • B. Only if no exception occurred
  • C. Only if except ran
  • D. Always, regardless of exceptions
Answer: D
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?
  • A. Creates a new ValueError named e
  • B. Catches ValueError and stores the exception object in variable e
  • C. Catches any exception and renames it ValueError
  • D. Compares the exception to variable e
Answer: B
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?
  • A. ValueError
  • B. NullPointerError
  • C. IndexError
  • D. KeyError
Answer: B
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?
  • A. try, else, except, finally
  • B. try, except, finally, else
  • C. try, except, else, finally
  • D. try, finally, except, else
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.
MCQ 9
What does raise do?
  • A. Catches an exception
  • B. Creates and throws an exception
  • C. Prints an error message
  • D. Terminates the program
Answer: B
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?
  • A. class MyError(Error): pass
  • B. class MyError(Exception): pass
  • C. def MyError(): raise Exception
  • D. exception MyError: pass
Answer: B
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?
  • A. No, only one except per try
  • B. Yes, but only two
  • C. Yes, as many as needed, each catching a different exception type
  • D. Yes, but they all must catch the same exception type
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.
MCQ 12
What happens if a finally block contains a return statement?
  • A. SyntaxError
  • B. The finally return is ignored
  • C. The finally return overrides the try/except return
  • D. Both return values are returned as a tuple
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.
MCQ 13
What exception is raised when you access a key that does not exist in a dictionary?
  • A. ValueError
  • B. IndexError
  • C. KeyError
  • D. LookupError
Answer: C
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?
  • A. Errors Are Found by Python
  • B. Easier to Ask Forgiveness than Permission
  • C. Exceptions Always Follow Patterns
  • D. Every Action Fails Predictably
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).
MCQ 15
Which of these exceptions is NOT a subclass of Exception?
  • A. ValueError
  • B. TypeError
  • C. KeyboardInterrupt
  • D. FileNotFoundError
Answer: C
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?
  • A. No, each except catches only one type
  • B. Yes, by listing them in a tuple: except (ValueError, TypeError)
  • C. Yes, by using the 'or' keyword: except ValueError or TypeError
  • D. Yes, by using a comma: except ValueError, TypeError
Answer: B
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?
  • A. ValueError
  • B. MathError
  • C. ZeroDivisionError
  • D. ArithmeticError
Answer: C
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?
  • A. There is no difference
  • B. SyntaxError is detected before execution; exceptions occur during execution
  • C. Exceptions are detected before execution; SyntaxError occurs during execution
  • D. SyntaxError can be caught with try/except; exceptions cannot
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.
MCQ 19
If a try block has both else and finally, and no exception occurs, what is the execution order?
  • A. try, finally, else
  • B. try, else, finally
  • C. else, try, finally
  • D. try, finally (else is skipped)
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.
MCQ 20
What is the purpose of 'raise' without any argument inside an except block?
  • A. It raises a new generic Exception
  • B. It re-raises the currently caught exception
  • C. It stops the except block
  • D. It is a SyntaxError
Answer: B
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

Easy
Write 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

Easy
Write 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

Easy
Write 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

Medium
Write 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

Medium
Create 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

Medium
Write 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

Hard
Write 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

Hard
Write 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 Notes

Want to learn Python with a live mentor?

Explore our Python course