Free Python Institute PCAP Practice Questions: Section 2: Exceptions
Practice 10 free Python Institute PCAP - Certified Associate Python Programmer (PCAP-31-03) questions on Section 2: Exceptions, with answers, explanations, and the IT Mastery next step.
Try the IT Mastery web app for a richer interactive practice experience with mixed sets, timed mocks, topic drills, explanations, and progress tracking.
Topic snapshot
| Field | Detail |
|---|---|
| Practice target | Python Institute PCAP |
| Topic area | Section 2: Exceptions |
| Blueprint weight | 14% |
| Page purpose | Focused sample questions before returning to mixed practice |
How to use this topic drill
Use this page to isolate Section 2: Exceptions for Python Institute PCAP. Work through the 10 questions first, then review the explanations and return to mixed practice in IT Mastery.
| Pass | What to do | What to record |
|---|---|---|
| First attempt | Answer without checking the explanation first. | The fact, rule, calculation, or judgment point that controlled your answer. |
| Review | Read the explanation even when you were correct. | Why the best answer is stronger than the closest distractor. |
| Repair | Repeat only missed or uncertain items after a short break. | The pattern behind misses, not the answer letter. |
| Transfer | Return to mixed practice once the topic feels stable. | Whether the same skill holds up when the topic is no longer obvious. |
Blueprint context: 14% of the practice outline. A focused topic score can overstate readiness if you recognize the pattern too quickly, so use it as repair work before timed mixed sets.
Sample questions
These are original IT Mastery practice questions aligned to this topic area. They are not official Python Institute questions, copied live-exam content, or exam dumps. Use them to preview question style and explanation depth before continuing with topic drills, mixed sets, and timed mocks in IT Mastery.
Question 1
Topic: Section 2: Exceptions
A developer is verifying a standard-library import in a package startup script. What is printed by this code?
try:
import math as m
print(m.sqrt(16))
except ImportError:
print("import failed")
else:
print(m.__name__)
finally:
print("finished")
Options:
A.
4.0,finishedB.
math,4.0,finishedC.
4.0,import failed,finishedD.
4.0,math,finished
Best answer: D
Explanation: The else clause in a try statement runs only when the try block completes with no exception. Here, importing math succeeds and m.sqrt(16) prints 4.0, so the else block prints math, and finally prints finished.
The key concept is that try ... except ... else ... finally has a distinct no-error path. In this code, import math as m succeeds, and print(m.sqrt(16)) prints 4.0. Because no ImportError occurs, the except block is skipped and the else block runs, printing m.__name__, which is math. After that, the finally block always runs and prints finished.
So the execution order is:
- run the
tryblock - skip
exceptbecause no exception occurred - run
else - run
finally
The closest mistake is assuming else is optional cleanup, but cleanup belongs to finally, not else.
- The option omitting
mathmisses thatelseruns on the no-exception path. - The option showing
import failedafter4.0is impossible because theexceptblock does not run when thetryblock succeeds. - The option printing
mathbefore4.0reverses the execution order; thetryblock prints first, thenelse, thenfinally.
Question 2
Topic: Section 2: Exceptions
What is printed by this Python 3 code?
for mode in ("existing", "new"):
saved = None
try:
try:
1 / 0
except ZeroDivisionError as ex:
saved = ex
if mode == "existing":
raise ex
else:
raise ZeroDivisionError("again")
except ZeroDivisionError as err:
print(mode, saved is err)
Options:
A. It prints
existing Trueand thennew False.B. It prints
existing Trueand thennew True.C. It prints
existing Falseand thennew True.D. It prints
existing Falseand thennew False.
Best answer: A
Explanation: In the existing pass, raise ex raises the same exception instance that was caught, so saved is err is True. In the new pass, raise ZeroDivisionError("again") creates a different exception object, so the identity test is False.
The key concept is the difference between raising an existing exception object and raising a new one. Inside the inner except, ex refers to the caught ZeroDivisionError instance.
When the code executes raise ex, it raises that same object again. The outer except catches the identical instance, so saved is err evaluates to True.
When the code executes raise ZeroDivisionError("again"), Python creates a fresh exception instance. The outer except still catches a ZeroDivisionError, but it is not the same object stored in saved, so saved is err is False.
The important takeaway is that matching exception type does not mean matching object identity.
- Both
Truefails because two exceptions of the same type are not automatically the same object. - Both
Falsefails becauseraise exdoes not build a new exception; it raises the caught instance. - Reversed results fail because the new object is created only by
raise ZeroDivisionError("again"), not byraise ex.
Question 3
Topic: Section 2: Exceptions
A developer wants one handler to catch two specific exception types while allowing another custom subclass to fall through to a later handler. Which statement about this code is correct?
class ParseError(Exception):
pass
class EmptyField(ParseError):
pass
class BadField(ParseError):
pass
def check(kind):
try:
if kind == "empty":
raise EmptyField("name")
elif kind == "bad":
raise BadField("name")
elif kind == "value":
raise ValueError("name")
except (EmptyField, ValueError) as err:
print("group", type(err).__name__, err.args[0])
except ParseError as err:
print("parse", type(err).__name__, err.args[0])
else:
print("ok")
finally:
print("end")
Options:
A. The grouped handler runs for
check("empty")andcheck("value").B. The
finallyblock runs only whenkindcauses an exception.C. The
elseblock runs aftercheck("bad")because the exception is handled.D. The grouped handler also runs for
check("bad")becauseBadFieldis aParseError.
Best answer: A
Explanation: A grouped except clause catches any exception type named in its tuple. In this code, EmptyField and ValueError match the grouped handler, while BadField is handled later by except ParseError because it is not part of the tuple.
Python checks except clauses from top to bottom. A grouped handler like except (EmptyField, ValueError) as err: is chosen when the raised exception is an instance of either listed type. That means check("empty") and check("value") both enter the same handler, and err.args[0] would contain "name".
BadField does not match that tuple, even though it is a subclass of ParseError, so execution continues to the next compatible handler: except ParseError as err:. The else block is separate from exception handling and runs only when the try block finishes without raising any exception. The finally block runs every time, whether an exception occurs or not.
The key takeaway is that a grouped except handles only the types explicitly listed there, not every related type in the hierarchy.
- The claim about
BadFieldfails because being aParseErrordoes not make it match the tuple(EmptyField, ValueError). - The claim about
elsefails becauseelseruns only when thetryblock raises no exception at all. - The claim about
finallyfails becausefinallyexecutes after both normal and exceptional flow.
Question 4
Topic: Section 2: Exceptions
A developer uses a custom exception hierarchy to classify validation failures:
class AppError(Exception):
pass
class InputError(AppError):
pass
class EmptyNameError(InputError):
pass
try:
raise EmptyNameError("name is required")
except AppError as err:
print(type(err).__name__)
print(err.__class__.__bases__[0].__name__)
Which statement is correct about what these exception classes represent?
Options:
A.
EmptyNameError.__bases__means the raised object belongs only toInputError, not toEmptyNameError.B.
InputErrorandAppErrorare class variables that store details forEmptyNameErrorobjects.C.
except AppErrorreclassifies the raised object as anAppErrorinstance before assigning it toerr.D.
EmptyNameErroris the most specific error category, anderris still an instance of it when caught byexcept AppError.
Best answer: D
Explanation: Exception classes are ordinary classes used to group related error conditions. Here, EmptyNameError names the specific category, while the object bound to err is the actual raised instance and keeps its original class even though a base-class handler catches it.
In Python, an exception class represents a type or category of error, not a single error event. The actual event is the exception object created from that class. In this hierarchy, EmptyNameError is a specialized kind of InputError, which is itself a kind of AppError.
When raise EmptyNameError("name is required") runs, the raised object is an instance of EmptyNameError. The except AppError as err clause catches it because subclass instances also match base-class handlers. That match does not convert the object into an AppError instance.
So type(err).__name__ stays EmptyNameError, while err.__class__.__bases__[0].__name__ shows its immediate parent class, InputError. The key idea is: exception classes classify errors; exception instances represent specific occurrences of those errors.
- The option claiming the handler changes the object’s class fails because
except AppErroronly matches by inheritance. - The option treating
InputErrorandAppErroras class variables fails because they are superclass exception types. - The option using
__bases__to replace the runtime class fails because__bases__describes parent classes, not object conversion.
Question 5
Topic: Section 2: Exceptions
A developer expects bad number when invalid input is processed, but that message never appears; for "x" the program prints generic failure instead.
values = ["4", "x"]
for v in values:
try:
print(12 / int(v))
except Exception:
print("generic failure")
except ValueError:
print("bad number")
What is the best fix?
Options:
A. Use
raiseinsideexcept Exceptionto letexcept ValueErrorrun.B. Replace
except Exceptionwithexcept BaseException.C. Move
int(v)outside thetryblock.D. Place
except ValueErrorbeforeexcept Exception.
Best answer: D
Explanation: Python checks except clauses from top to bottom. Here, int("x") raises ValueError, but except Exception catches it first because ValueError inherits from Exception. The specific handler must come before the general one.
The core rule is exception-handler ordering: Python executes the first except clause whose type matches the raised exception. In this code, int("x") raises ValueError. Because ValueError is part of the Exception hierarchy, the earlier except Exception clause already matches, so the later except ValueError clause is effectively unreachable for that error.
- Put more specific exception classes first.
- Put broader fallback handlers later.
- Use a generic
except Exceptiononly as a last resort.
A common mistake is assuming Python will keep checking later handlers after entering a broad one, but it does not.
- Re-raise idea fails because raising from the broad handler sends the exception outward; it does not continue to a later sibling
except. - Broader catch with
BaseExceptionmakes the problem worse by catching even more cases before the specific handler. - Move conversion out would prevent this
tryblock from handling theValueErrorat all.
Question 6
Topic: Section 2: Exceptions
A utility should return "missing" for either a missing dictionary key or an out-of-range list index. After a refactor, it now ends with an uncaught IndexError:
def get_value(container, key):
try:
return container[key]
except KeyError:
return "missing"
print(get_value(["A", "B"], 5))
Which change is the best fix?
Options:
A. Replace
except KeyErrorwithexcept LookupError.B. Add
finally: return "missing"after theexceptblock.C. Replace
except KeyErrorwithexcept ArithmeticError.D. Replace
return container[key]withraise KeyError(key).
Best answer: A
Explanation: except matches the named exception class and its subclasses. IndexError is not a subclass of KeyError, but both IndexError and KeyError inherit from LookupError, so catching LookupError handles both cases required by the function.
Python uses the exception hierarchy when deciding whether an except block handles a raised exception. A handler for KeyError catches KeyError and any subclasses of KeyError, but it does not catch sibling exceptions. Here, list access with an invalid index raises IndexError, while missing dictionary access raises KeyError.
Both of those exceptions share the same parent:
LookupErrorKeyErrorIndexError
So if one function must handle both failed dictionary lookups and failed sequence indexing, except LookupError is the right common handler. A broader catch like Exception would also work, but it would handle unrelated errors too.
The key idea is that shared parent classes can be used to catch multiple related exception types.
- Wrong branch using
ArithmeticErrorfails because lookup problems are unrelated to arithmetic exceptions. - Always returns adding
finally: return "missing"would override normal successful results too. - Changes the problem raising
KeyErrormanually does not fix the hierarchy mismatch; it just forces a different exception.
Question 7
Topic: Section 2: Exceptions
A developer is debugging why a specific handler never runs:
class AppError(Exception):
pass
class ConfigError(AppError):
pass
try:
raise ConfigError("missing file")
except AppError as err:
print("app:", type(err).__name__)
except ConfigError as err:
print("config:", err)
finally:
print("cleanup")
Which change should be made so the ConfigError handler becomes reachable while other AppError exceptions are still handled?
Options:
A. Move
except ConfigError as err:aboveexcept AppError as err:.B. Replace
finallywithelse.C. Change
except AppError as err:toexcept Exception as err:.D. Re-raise
errinside theAppErrorhandler.
Best answer: A
Explanation: Python checks except clauses from top to bottom. Because ConfigError inherits from AppError, the broader AppError handler matches first and the later specific handler is never reached. Reordering them fixes the flow.
In Python, exception handlers are tested in order, and the first matching except block runs. Here, ConfigError is a subclass of AppError, so except AppError as err: already matches the raised ConfigError. That makes the later except ConfigError as err: effectively unreachable.
To preserve specific handling and still catch other related exceptions, order handlers from most specific to most general:
except ConfigError as err:except AppError as err:
If one handler runs, Python does not continue searching later except clauses in the same try. The finally block is separate and still executes either way. The key takeaway is to place subclass exception handlers before superclass handlers.
- Catching
Exceptionmakes the first handler even broader, so the specific handler still cannot run. - Re-raising from the first handler sends the exception outward; Python does not retry later handlers in the same
try. - Replacing
finallywithelsechanges when that block runs, but it does not solve the handler-ordering problem.
Question 8
Topic: Section 2: Exceptions
A developer wants to inspect the values stored in an exception object. What is printed by this Python 3 code?
data = None
try:
raise IndexError("row", 5)
except IndexError as err:
data = err.args
print(data[0], data[1], type(data).__name__)
Options:
A.
IndexError: ('row', 5)B.
row 5 listC.
('row', 5) tupleD.
row 5 tuple
Best answer: D
Explanation: In an except ... as block, the name after as refers to the exception object. Its args property stores the positional arguments passed when the exception was raised, and that value is a tuple.
When raise IndexError("row", 5) executes, Python creates an IndexError object with two positional arguments. Inside except IndexError as err, err is that exception instance, and err.args contains those arguments as a tuple: ("row", 5).
The code assigns that tuple to data, then prints:
data[0]→rowdata[1]→5type(data).__name__→tuple
So the printed result is row 5 tuple. The closest trap is confusing err.args with printing the exception object itself, which would show the tuple representation instead of separate indexed values.
- Whole tuple printed would require printing
data, notdata[0]anddata[1]separately. - List confusion fails because exception
argsare stored in a tuple, not a list. - Unhandled exception fails because the matching
except IndexErrorblock catches the raised exception.
Question 9
Topic: Section 2: Exceptions
Consider this code. Which statement correctly identifies the handled exception and the exception that propagates beyond the try/except statement?
from math import sqrt
try:
print(sqrt(-1))
except ImportError:
print("import")
except ValueError:
print("value")
print(math.pi)
Options:
A.
ValueErroris handled, thenNameErrorpropagates.B.
ValueErrorpropagates because the firstexceptdoes not match.C.
ImportErroris handled, thenNameErrorpropagates.D.
ValueErroris handled, then the program ends normally.
Best answer: A
Explanation: The call sqrt(-1) raises a ValueError, and that exception is handled by the matching except ValueError clause. After the try/except finishes, print(math.pi) runs outside it, and because only sqrt was imported, math is undefined, so a NameError propagates.
This tests the difference between an exception handled inside a try/except block and one raised later with no matching handler. In from math import sqrt, only the name sqrt is added to the current namespace; the name math is not.
Execution goes like this:
sqrt(-1)raisesValueError.except ValueErrorcatches it and printsvalue.- Control continues after the
try/exceptblock. print(math.pi)then raisesNameErrorbecausemathwas never imported as a module name.
So the ValueError is handled, but the later NameError is not. The closest mistake is assuming that importing one member from a module also defines the module’s qualified name.
- The option claiming
ImportErroris handled confuses a successful import with the later runtime error fromsqrt(-1). - The option claiming normal termination ignores that
from math import sqrtdoes not define the namemath. - The option claiming
ValueErrorpropagates misunderstands handler matching;exceptorder matters for subclasses, not for unrelated exceptions here.
Question 10
Topic: Section 2: Exceptions
A developer adds a custom exception so existing lookup-related handlers keep working. What is printed by this Python 3 code?
class MissingKeyError(KeyError):
pass
def fetch(data, key):
if key not in data:
raise MissingKeyError(key)
return data[key]
try:
print(fetch({"x": 1}, "y"))
except LookupError:
print("lookup")
except Exception:
print("other")
Options:
A. lookup
B.
MissingKeyErrortracebackC. y
D. other
Best answer: A
Explanation: Exception handlers match subclasses as well as the exact named class. Because MissingKeyError extends KeyError, and KeyError is a LookupError, the existing except LookupError block catches it and prints lookup.
In Python, an except clause handles instances of the named exception class and any of its subclasses. Here, the custom MissingKeyError is integrated into the built-in hierarchy by inheriting from KeyError. Since KeyError is a subclass of LookupError, raising MissingKeyError also satisfies except LookupError.
When fetch({"x": 1}, "y") runs, the key is missing, so the function raises MissingKeyError before returning any value. That means print(fetch(...)) never prints a dictionary value. The generic except Exception block is not reached because Python uses the first matching handler.
otheris tempting becauseExceptionis a superclass, but the earlierLookupErrorhandler already matches.ywould require the function to return a value, but it raises an exception before any return occurs.- The traceback option fails because a custom exception does not need its own
exceptblock when a superclass handler already covers it.
Continue in the web app
Use IT Mastery for interactive Python Institute PCAP practice with mixed sets, timed mocks, topic drills, explanations, and progress tracking.
Try Python Institute PCAP on Web