PCAP-31-03: Section 2: Exceptions

Try 10 focused PCAP-31-03 questions on Section 2: Exceptions, with explanations, then continue with IT Mastery.

On this page

Open the matching IT Mastery practice page for timed mocks, topic drills, progress tracking, explanations, and full practice.

Try PCAP-31-03 on Web View full PCAP-31-03 practice page

Topic snapshot

FieldDetail
Exam routePCAP-31-03
Topic areaSection 2: Exceptions
Blueprint weight14%
Page purposeFocused sample questions before returning to mixed practice

How to use this topic drill

Use this page to isolate Section 2: Exceptions for PCAP-31-03. Work through the 10 questions first, then review the explanations and return to mixed practice in IT Mastery.

PassWhat to doWhat to record
First attemptAnswer without checking the explanation first.The fact, rule, calculation, or judgment point that controlled your answer.
ReviewRead the explanation even when you were correct.Why the best answer is stronger than the closest distractor.
RepairRepeat only missed or uncertain items after a short break.The pattern behind misses, not the answer letter.
TransferReturn 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 questions are original IT Mastery practice items aligned to this topic area. They are designed for self-assessment and are not official exam questions.

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, finished

  • B. math, 4.0, finished

  • C. 4.0, import failed, finished

  • D. 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 try block
  • skip except because 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 math misses that else runs on the no-exception path.
  • The option showing import failed after 4.0 is impossible because the except block does not run when the try block succeeds.
  • The option printing math before 4.0 reverses the execution order; the try block prints first, then else, then finally.

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 True and then new False.

  • B. It prints existing True and then new True.

  • C. It prints existing False and then new True.

  • D. It prints existing False and then new 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 True fails because two exceptions of the same type are not automatically the same object.
  • Both False fails because raise ex does 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 by raise 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") and check("value").

  • B. The finally block runs only when kind causes an exception.

  • C. The else block runs after check("bad") because the exception is handled.

  • D. The grouped handler also runs for check("bad") because BadField is a ParseError.

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 BadField fails because being a ParseError does not make it match the tuple (EmptyField, ValueError).
  • The claim about else fails because else runs only when the try block raises no exception at all.
  • The claim about finally fails because finally executes 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 to InputError, not to EmptyNameError.

  • B. InputError and AppError are class variables that store details for EmptyNameError objects.

  • C. except AppError reclassifies the raised object as an AppError instance before assigning it to err.

  • D. EmptyNameError is the most specific error category, and err is still an instance of it when caught by except 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 AppError only matches by inheritance.
  • The option treating InputError and AppError as 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 raise inside except Exception to let except ValueError run.

  • B. Replace except Exception with except BaseException.

  • C. Move int(v) outside the try block.

  • D. Place except ValueError before except 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 Exception only 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 BaseException makes the problem worse by catching even more cases before the specific handler.
  • Move conversion out would prevent this try block from handling the ValueError at 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 KeyError with except LookupError.

  • B. Add finally: return "missing" after the except block.

  • C. Replace except KeyError with except ArithmeticError.

  • D. Replace return container[key] with raise 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:

  • LookupError
  • KeyError
  • IndexError

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 ArithmeticError fails because lookup problems are unrelated to arithmetic exceptions.
  • Always returns adding finally: return "missing" would override normal successful results too.
  • Changes the problem raising KeyError manually 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: above except AppError as err:.

  • B. Replace finally with else.

  • C. Change except AppError as err: to except Exception as err:.

  • D. Re-raise err inside the AppError handler.

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 Exception makes 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 finally with else changes 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 list

  • C. ('row', 5) tuple

  • D. 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]row
  • data[1]5
  • type(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, not data[0] and data[1] separately.
  • List confusion fails because exception args are stored in a tuple, not a list.
  • Unhandled exception fails because the matching except IndexError block 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. ValueError is handled, then NameError propagates.

  • B. ValueError propagates because the first except does not match.

  • C. ImportError is handled, then NameError propagates.

  • D. ValueError is 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) raises ValueError.
  • except ValueError catches it and prints value.
  • Control continues after the try/except block.
  • print(math.pi) then raises NameError because math was 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 ImportError is handled confuses a successful import with the later runtime error from sqrt(-1).
  • The option claiming normal termination ignores that from math import sqrt does not define the name math.
  • The option claiming ValueError propagates misunderstands handler matching; except order 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. MissingKeyError traceback

  • C. 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.

  • other is tempting because Exception is a superclass, but the earlier LookupError handler already matches.
  • y would 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 except block when a superclass handler already covers it.

Continue with full practice

Use the PCAP-31-03 Practice Test page for the full IT Mastery route, mixed-topic practice, timed mock exams, explanations, and web/mobile app access.

Try PCAP-31-03 on Web View PCAP-31-03 Practice Test

Free review resource

Read the PCAP-31-03 Cheat Sheet on Tech Exam Lexicon, then return to IT Mastery for timed practice.

Revised on Thursday, May 14, 2026