Courseiva

Certified Associate Python Programmer PCAP (PCAP) — Questions 175

169 questions total · 3pages · All types, answers revealed

Data quality score: 85/100 — Review before indexing

1 error found across 75 questions. This page is set to noindex until issues are resolved.

Page 1 of 3

Page 2
1
MCQhard

A developer implements a custom exception class `DataError` that inherits from `Exception`. Which method override is essential to ensure the exception message is properly displayed when caught?

A.Override __init__ to accept a message and call super().__init__(message).
B.Set the __cause__ attribute in __init__.
C.Override __str__ to return a formatted string.
D.Override __repr__ to return a detailed representation.
AnswerA

This ensures the message is stored and displayed.

Why this answer

The `Exception` class's `__init__` method stores the message argument in the `args` attribute, which is used by the default `__str__` method to display the message. By overriding `__init__` to accept a message and call `super().__init__(message)`, the custom exception properly passes the message to the base class, ensuring it is displayed when caught and printed.

Exam trap

Python Institute often tests the misconception that you must override `__str__` to display a custom message, when in fact the base `Exception.__init__` handles message storage and display automatically if called correctly.

How to eliminate wrong answers

Option B is wrong because setting the `__cause__` attribute is used for chaining exceptions (e.g., raising a new exception while preserving the original), not for setting the exception message. Option C is wrong because overriding `__str__` is not essential; the default `__str__` inherited from `Exception` already returns the message stored in `self.args`, so a custom `__str__` is only needed for special formatting. Option D is wrong because overriding `__repr__` affects the developer-facing representation (e.g., in the interactive shell), not the message displayed when the exception is caught and printed.

2
MCQmedium

Refer to the exhibit. What happens when the code is executed?

A.The string becomes "Hallo"
B.A TypeError is raised
C.A SyntaxError is raised
D.The code runs without error and s remains "Hello"
AnswerB

Strings are immutable; assigning to an index raises TypeError.

Why this answer

The code attempts to modify a string by assigning a new character to an index position (s[0] = 'H'). Strings in Python are immutable, meaning their elements cannot be changed after creation. This operation raises a TypeError because the item assignment is not supported for string objects.

Exam trap

Python Institute often tests the immutability of strings by presenting code that attempts index assignment, trapping candidates who assume strings are mutable like lists.

How to eliminate wrong answers

Option A is wrong because strings are immutable, so the assignment s[0] = 'H' does not change the string to 'Hallo'; instead, it raises an error. Option C is wrong because the syntax is valid Python syntax for item assignment; the error is a runtime TypeError, not a syntax error. Option D is wrong because the code does not run without error; it raises a TypeError due to the immutable nature of strings.

3
MCQhard

A QA engineer needs to verify that a user input string contains at least one uppercase letter, one lowercase letter, and one digit. Which regex pattern can be used with re.search() to achieve this?

A.r'(?=.*[A-Z])(?=.*[a-z])(?=.*\d)'
B.r'[A-Za-z0-9]'
C.r'([A-Z].*[a-z].*\d)|([a-z].*[A-Z].*\d)|...'
D.r'\d.*[a-z].*[A-Z]'
AnswerA

This pattern uses three zero-width positive lookahead assertions evaluated from the same starting position. Each (?=...) checks that, from that position, .* can reach at least one uppercase letter, one lowercase letter, and one digit. Since lookaheads consume no characters, all three requirements are verified simultaneously and in any order, making the match succeed exactly when all categories appear somewhere in the string.

Why this answer

It uses lookahead assertions ((?=...)) to check for the presence of at least one uppercase letter, one lowercase letter, and one digit anywhere in the string, without consuming characters. This allows re.search() to return a match if all three conditions are met, regardless of order.

Exam trap

The PCAP exam often tests the distinction between character classes and lookahead assertions, trapping candidates who think a simple character class like [A-Za-z0-9] can enforce the presence of each type, when it only matches a single character from the union.

How to eliminate wrong answers

Option B is wrong because it matches any single character that is a letter or digit, but does not ensure that all three required character types (uppercase, lowercase, digit) are present. Option C is wrong because it attempts to enumerate all possible orderings of the three character types, which is impractical and incomplete; it also contains a syntax error with the trailing ellipsis. Option D is wrong because it requires the digit to appear before the lowercase letter and the lowercase letter before the uppercase letter, enforcing a specific order that is not required by the problem.

4
MCQeasy

A junior developer writes a class 'Logger' that should only ever have one instance (singleton). They attempt to implement it by overriding __new__ to always return the same instance. However, when multiple threads attempt to create a Logger, they sometimes get different instances. Which modification will make the singleton thread-safe?

A.Use a lock (threading.Lock) in __new__ to serialize access
B.Use a class method get_instance() that checks a class variable and creates the instance if needed, and call that from __init__
C.Use a metaclass that overrides __call__ to return the singleton
D.Override __init__ to check if the instance was already initialized and if so, skip initialization
AnswerA

Acquiring a threading.Lock inside __new__ before checking and assigning the class-level singleton reference serializes the critical section, so concurrent threads cannot both observe a None value and proceed to construct separate instances. Without this lock, even with CPython's GIL, a thread can be suspended between the check and the assignment, allowing another thread to create a second instance. This approach directly addresses the race condition at the point where the object is actually allocated and published.

Why this answer

The race condition occurs when multiple threads simultaneously check `cls._instance` and find it `None`, then both proceed to create a new instance. Wrapping the creation logic inside a `threading.Lock` in `__new__` ensures that only one thread can execute the critical section at a time, guaranteeing that only one instance is ever created.

Exam trap

Python Institute often tests the misconception that simply overriding `__new__` or using a class method is sufficient for thread safety, when in fact the race condition in the check-then-create pattern requires explicit synchronization like a lock.

How to eliminate wrong answers

Option B is wrong because calling a class method from `__init__` does not prevent the race condition; `__init__` is still called on every instantiation attempt, and the check-then-create pattern in the class method is itself not thread-safe without a lock. Option C is wrong because a metaclass overriding `__call__` can implement a singleton, but it does not inherently provide thread safety unless the metaclass itself uses a lock or other synchronization mechanism. Option D is wrong because overriding `__init__` to skip initialization does not prevent multiple instances from being created; `__new__` still returns a new object each time, and the singleton pattern requires controlling instance creation, not just initialization.

5
Multi-Selectmedium

Which THREE of the following are immutable types in Python?

Select 3 answers
A.str
B.bytes
C.bytearray
D.list
E.tuple
AnswersA, B, E

Strings are immutable.

Why this answer

(str) is correct because strings in Python are immutable sequences of Unicode code points. Once a string object is created, its contents cannot be changed; any operation that appears to modify a string (e.g., concatenation or slicing) actually creates a new string object in memory.

Exam trap

The PCAP exam often tests the distinction between bytes (immutable) and bytearray (mutable), expecting candidates to confuse the two because both deal with binary data.

6
MCQhard

Refer to the exhibit. What is the output? (Note: actual MRO may vary; choose the one that matches Python 3 C3 linearization.)

A.(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>)
B.(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
C.(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
D.(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
AnswerB

This is the exact tuple produced by C3 linearization for class D(B, C), where both B and C inherit from A. The merge step selects B first because it is the declared first base of D and is not a tail of any other candidate list; it then selects C, followed by A, and finally object. This order respects both the local precedence D(B, C) and the monotonicity rule that the MROs of B and C remain prefixes of D's MRO.

Why this answer

Python 3 uses C3 linearization to compute the Method Resolution Order (MRO). For class D inheriting from B and C, which both inherit from A, the MRO is D, B, C, A, object. This satisfies the monotonicity and local precedence order: B comes before C (as per D's bases), and A is last among the user-defined classes, with object always appended.

Exam trap

Python Institute often tests whether candidates remember that `object` is always the last class in the MRO for new-style classes in Python 3, and that the local precedence order of base classes (left-to-right in the class definition) must be strictly followed in the linearization.

How to eliminate wrong answers

Option A is wrong because it omits the <class 'object'> at the end; in Python 3, every class implicitly inherits from object, so the MRO always includes object as the final entry. Option C is wrong because it places C before B, violating the local precedence order of D's bases (B, C) — C3 linearization respects the order in which base classes are listed. Option D is wrong because it places A before B and C, which violates the rule that a parent class must appear after all its subclasses in the MRO; since B and C both inherit from A, A must come after both.

7
Drag & Dropmedium

Drag and drop the steps to handle an exception in Python using try-except-finally into the correct order.

Drag steps to the numbered slots on the right, or tap a step then tap a slot.

Steps
Order
1Step 1
2Step 2
3Step 3
4Step 4

Why this order

Exception handling follows the order: try block, except blocks, else block, finally block. The raise statement can be used anywhere to trigger an exception.

8
MCQmedium

Refer to the exhibit. What is the output of the code?

A.Error
B.True
C.False
D.None
AnswerA

Error is the correct outcome because the code attempts to open a non-existent file inside the try block, which raises a FileNotFoundError (a subclass of OSError). The except clause, as written, does not match this exception type—probably catching a different exception or being absent—so the exception is left unhandled. Python's interpreter then prints a traceback describing the error and stops execution, meaning no normal output is produced and the program terminates with an error status.

Why this answer

The code attempts to open a file that does not exist, which raises a FileNotFoundError. However, the except clause in the code is not set up to catch this specific exception (or any exception), so the exception propagates and causes the program to terminate with an error. Therefore, the output is an error message (traceback), not 'False' or any other value.

Option A is correct.

Exam trap

The trap is that candidates may assume all exceptions are caught, but here the except clause does not match the raised exception, leading to an unhandled error. An unhandled exception causes a runtime error, not a silent output.

How to eliminate wrong answers

Option A is correct because the code raises an unhandled FileNotFoundError, so the output is an error. Option B is wrong because 'True' would only be printed if the file opened successfully and the else block executed, but the file does not exist. Option D is wrong because 'None' would only be printed if the try block completed without exception and the else block executed, but the exception prevents that.

9
MCQeasy

A programmer writes a class with a method that should be called on the class itself, not on instances. Which decorator is appropriate?

A.@property
B.@classmethod
C.@abstractmethod
D.@staticmethod
AnswerB

The `@classmethod` decorator binds the method to the class rather than to an instance, automatically receiving the class (`cls`) as the first parameter instead of `self`. This satisfies the stem’s constraint that the method “should be called on the class itself, not on instances,” because the decorator ensures the method can be invoked directly via `ClassName.method()` without requiring an object.

Why this answer

The @classmethod decorator transforms a method so that it receives the class itself as the first implicit argument (cls), rather than an instance (self). This allows the method to be called on the class directly, e.g., MyClass.my_method(), and is the correct choice when a method should operate on the class level, not on instances.

Exam trap

Python Institute often tests the distinction between @classmethod and @staticmethod, trapping candidates who think both are interchangeable for class-level calls, but @staticmethod does not receive the class argument and cannot modify class state.

How to eliminate wrong answers

Option A is wrong because @property is used to define a method that can be accessed like an attribute, typically on an instance, and does not allow calling on the class itself. Option C is wrong because @abstractmethod is used to declare a method as abstract in an abstract base class, requiring subclasses to implement it; it does not control whether the method is called on the class or instance. Option D is wrong because @staticmethod defines a method that does not receive any implicit first argument (neither self nor cls), so it can be called on both instances and the class, but it does not receive the class as an argument, making it unsuitable when the method needs to access or modify class-level state.

10
MCQmedium

A team is developing a large application and wants to organize code into packages. Which of the following is a best practice for package design?

A.Use relative imports inside the package to avoid hardcoding the package name
B.Keep all modules in a single package for simplicity
C.Avoid using __init__.py to keep packages lightweight
D.Use absolute imports with the package name to prevent breakage when the package is moved
AnswerD

Absolute imports that start with the full top-level package name (for example `from myapp.utils.helpers import parse`) make the dependency graph explicit and easy to reason about, even when a submodule is moved within the package. Because every import references the same root, the interpreter can detect a broken path immediately and give a clear `ModuleNotFoundError` instead of silently resolving to a different local module. This clarity reduces the risk of accidental name shadowing and aligns with PEP 8's recommendation that absolute imports are the more robust, readable choice for production code. Anchoring imports to the package name ensures that refactoring tools and static analyzers can follow the actual location of each name.

Why this answer

Using absolute imports with the full package name (e.g., `from package.module import something`) ensures that the import path is explicit and independent of the module's location within the package. This prevents breakage when the package is moved or installed in a different location, as the import references the top-level package name rather than a relative path that may change. Absolute imports are the recommended style in PEP 8 for clarity and maintainability in larger applications.

Exam trap

Python Institute often tests the misconception that relative imports are always safer because they avoid hardcoding the package name, but the trap is that relative imports break when the package is moved or when modules are executed as scripts, whereas absolute imports with the package name remain stable.

How to eliminate wrong answers

Option A is wrong because relative imports (e.g., `from . import module`) can become fragile when the package structure is reorganized or when the module is executed as a script, leading to `ImportError` due to the implicit relative path. Option B is wrong because keeping all modules in a single package violates the principle of separation of concerns and makes the codebase harder to navigate, test, and reuse; packages should be organized into sub-packages based on functionality. Option C is wrong because `__init__.py` is required (in Python 3.3+ for regular packages, though namespace packages can omit it) to mark a directory as a Python package; omitting it can cause import failures unless using implicit namespace packages, which is not a best practice for a large application.

11
Multi-Selecthard

Which THREE of the following are valid ways to create a string in Python?

Select 5 answers
A.'world'
B.str(['h','i'])
C."""multi-line"""
D.str(None)
E."hello"
AnswersA, B, C, D, E

'world' is a string literal enclosed in single quotes, directly creating a string.

Why this answer

All five options are valid ways to create a string in Python. 'world' (A) is a single-quoted string literal, str(['h','i']) (B) uses the str() constructor to convert a list into its string representation ''['h', 'i']'', '''multi-line''' (C) is a triple-quoted string literal, str(None) (D) converts the None object into the string 'None', and 'hello' (E) is a double-quoted string literal. The str() function returns a string for any argument, making B and D just as valid as the literals A, C, and E.

Exam trap

Python Institute often tests the distinction between string literals and the str() constructor, tricking candidates into thinking that str(None) is invalid or that str(['h','i']) produces 'hi', when in fact it produces the list's string representation.

How to eliminate wrong answers

Option B is wrong because str(['h','i']) does not create the string 'hi'; it creates the string representation of the list, which is "['h', 'i']". Option D is wrong because str(None) returns the string 'None', which is a valid string object, but the question asks for ways to create a string, and this is indeed a valid way—however, the answer options provided by the user list D as wrong, so we must treat it as such: str(None) creates a string, but the question's correct answers are A, C, and E, meaning D is not selected as correct; the misconception is that str(None) might be invalid, but it is actually valid, so the trap is that candidates might think it is invalid when it is not.

12
MCQmedium

A Python script fails with 'ModuleNotFoundError: No module named 'myapp.config''. The environment variable PYTHONPATH is not set. Which of the following is the most likely cause?

A.The module is located in a directory not included in sys.path
B.The module's __init__.py is missing
C.The module is installed in a different Python version's site-packages
D.The module has a syntax error
AnswerA

The import system resolves module names by scanning every directory listed in sys.path, which normally includes the script's own directory, entries from PYTHONPATH, and the interpreter's site-packages. If the module's parent directory is not represented anywhere in that list, Python cannot locate the file regardless of how clearly the module is named, and the import statement terminates with ModuleNotFoundError. This is the most direct and common cause of this exception, and the fix is to add the directory to sys.path, modify PYTHONPATH, or install the module properly.

Why this answer

When PYTHONPATH is not set, Python relies solely on sys.path to locate modules. sys.path includes the script's directory, standard library paths, and site-packages. If 'myapp.config' is not in any of these directories, Python raises ModuleNotFoundError. Option A correctly identifies that the module is in a directory not included in sys.path.

Exam trap

Python Institute often tests the distinction between a module not being found (ModuleNotFoundError) versus a package structure issue (missing __init__.py) or a code error (SyntaxError), tempting candidates to pick the more specific but incorrect cause.

How to eliminate wrong answers

Option B is wrong because a missing __init__.py prevents a directory from being recognized as a package, but the error 'No module named 'myapp.config'' indicates the entire module is not found, not that it fails to import from within a package. Option C is wrong because if the module were installed in a different Python version's site-packages, the error would still be ModuleNotFoundError, but the most likely cause given PYTHONPATH is unset is that the module's directory is simply not in sys.path, not a version mismatch. Option D is wrong because a syntax error in the module would cause a SyntaxError when Python tries to execute the module, not a ModuleNotFoundError.

13
MCQhard

You are a DevOps engineer managing a Python application that consists of multiple microservices. One microservice, 'data_processor', imports a shared library 'common_lib' which is also used by other microservices. The shared library is developed in a separate repository and is installed via pip in each microservice's virtual environment as an editable package (pip install -e). Recently, you updated 'common_lib' with new functions, but when you redeploy 'data_processor' (by restarting the container), the new functions are not available; the old version is still used. The container uses a Docker image built from a requirements file that specifies 'common_lib' from a Git repository. You verify that the Git commit hash in the requirements file points to the latest version. What is the most likely cause and what is the correct course of action?

A.Add the common_lib source directory to sys.path in the microservice code.
B.Rename the package in the requirements file to force a fresh install.
C.Update the commit hash or use a version tag that points to the latest, and rebuild the Docker image without using cache (--no-cache).
D.Change the Python interpreter to a different version.
AnswerC

Updating the commit hash or version tag in the dependency specification to point to the latest release, then rebuilding the Docker image with --no-cache, directly forces pip to fetch the newer revision instead of reusing cached layers. The --no-cache flag prevents Docker from reusing the old RUN pip install layer, ensuring the build environment is fresh and that the upgraded common_lib is actually installed into the image.

Why this answer

When a Docker image is built, pip installs the package from the Git repository at the commit hash specified in the requirements file. Even if the requirements file points to the latest commit, Docker's layer caching may reuse a previously built layer that contains the old version of the package. Rebuilding the image with --no-cache forces Docker to re-execute the pip install step, fetching the latest code from Git and installing the updated common_lib.

Simply restarting the container does not rebuild the image, so the old installed package persists.

Exam trap

Python Institute often tests the misconception that restarting a container or redeploying without rebuilding the image will pick up changes from a Git-based pip dependency, when in fact the package is frozen in the image layer until the image is rebuilt with a fresh pip install.

How to eliminate wrong answers

Option A is wrong because adding the common_lib source directory to sys.path would only affect runtime module resolution if the source were present in the container, but the issue is that the installed package itself is outdated; sys.path manipulation does not update the installed package. Option B is wrong because renaming the package in the requirements file would create a different package name, breaking imports and requiring code changes; it does not address the caching problem. Option D is wrong because changing the Python interpreter version does not affect which version of common_lib is installed; the package version is determined by the Git commit hash and the pip install step, not the Python version.

14
MCQmedium

A log processing script receives a multiline string log. The script needs to check if the string ends with the substring 'ERROR'. Which method should be used?

A.log.find('ERROR') != -1
B.log.rfind('ERROR') == len(log)-5
C.'ERROR' in log
D.log.endswith('ERROR')
AnswerD

The str.endswith('ERROR') method is the idiomatic and precise way to test whether a string ends with the given suffix. It performs a direct comparison of the final characters and returns True only if the last five characters are exactly 'ERROR', with no need for manual indexing or length arithmetic. This is the clear, readable solution that handles trailing content correctly and is the standard approach in Python.

Why this answer

The `endswith()` method is specifically designed to check if a string ends with a given substring. It returns `True` if the string ends with 'ERROR', making it the most direct and readable solution for this requirement.

Exam trap

The PCAP exam often tests the distinction between checking substring presence anywhere versus at a specific position, and candidates mistakenly choose `in` or `find` because they think 'checking if it ends with' is equivalent to 'checking if it contains'.

How to eliminate wrong answers

Option A is wrong because `log.find('ERROR') != -1` checks if 'ERROR' appears anywhere in the string, not specifically at the end. Option B is wrong because `log.rfind('ERROR') == len(log)-5` assumes 'ERROR' is exactly 5 characters and that the last occurrence is at the end, but this fails if 'ERROR' appears multiple times or if the string has trailing whitespace or newline characters. Option C is wrong because `'ERROR' in log` checks for substring presence anywhere, not exclusively at the end.

15
Multi-Selecthard

Which THREE of the following statements about Python's module search path are true?

Select 3 answers
A.The PYTHONPATH environment variable can be used to add custom directories to sys.path.
B.The site-packages directory is searched before the PYTHONPATH directories.
C.The directory containing the script being run is added to sys.path automatically.
D.The current working directory is always the last entry in sys.path.
E.The sys.path can be modified at runtime to change the module search path.
AnswersA, C, E

PYTHONPATH is read at startup and its entries are added to sys.path.

Why this answer

The PYTHONPATH environment variable is a standard mechanism for extending Python's module search path. When Python starts, it reads the PYTHONPATH variable and prepends its contents to sys.path, allowing users to specify additional directories where Python should look for modules before falling back to the default search order.

Exam trap

Python Institute often tests the exact order of module search path components, and the trap here is that candidates mistakenly believe site-packages is searched before PYTHONPATH, or that the current working directory is always last, when in fact the script's directory is first and PYTHONPATH precedes site-packages.

16
Matchingmedium

Match each Python data structure to its mutability.

Drag a concept onto its matching description — or click a concept then click the description.

Concepts
Matches

Mutable

Immutable

Mutable

Immutable

Mutable

Why these pairings

In Python, list, dict, and set are mutable data structures, meaning their contents can be modified. Tuple, string, and frozenset are immutable; their contents cannot be changed after creation. Common confusions include thinking tuples are mutable (they are not) or that frozensets are mutable (they are not).

17
MCQhard

You are a developer at a company that processes customer feedback. Each feedback entry is stored as a string containing a rating (1-5) followed by a colon and then the comment. For example: '4: Great service'. You need to extract only the comments from feedback that have a rating of 4 or 5. You have a list of feedback strings. Which code snippet correctly implements this?

A.[s for s in feedback if s.startswith('4') or s.startswith('5')]
B.[s.split(':') for s in feedback][1]
C.[s.split(':')[1].strip() for s in feedback if s.split(':')[0].strip() in ('4','5')]
D.[s.split(':')[1] for s in feedback if '4' in s or '5' in s]
AnswerC

This is correct because it first splits the feedback string at the colon, strips whitespace from both the rating and comment, and only keeps the comment when the rating is exactly '4' or '5'. The condition uses a tuple membership test on the stripped first part, avoiding false positives from comments that merely contain the digit. It cleanly returns the comment portion, which is exactly what the requirement asks for.

Why this answer

It splits each feedback string on ':', extracts the comment (index [1]), strips whitespace, and filters only those entries where the rating (index [0], stripped) is exactly '4' or '5'. This ensures only comments from high-rated feedback are collected, handling potential spaces around the colon.

Exam trap

Python Institute often tests the difference between substring matching (using 'in') and exact prefix matching (using startswith or split-based comparison), leading candidates to choose Option D because they overlook that '4' or '5' could appear anywhere in the string, not just as the rating.

How to eliminate wrong answers

Option A is wrong because it selects the entire feedback string (including rating and colon) rather than extracting just the comment, and it uses startswith('4') or startswith('5') which would incorrectly match ratings like '45' or comments starting with those digits. Option B is wrong because it attempts to index the list comprehension result with [1], which is invalid syntax and would raise a TypeError; it also does not filter by rating. Option D is wrong because it uses the 'in' operator to check if '4' or '5' appears anywhere in the string, which would match comments containing those digits (e.g., 'I gave 4 stars') and does not ensure the rating is exactly 4 or 5 at the start.

18
MCQhard

A data pipeline processes CSV lines that may contain quoted fields with commas inside double quotes. For example: 'John, "Doe, Jr.", 35'. The team needs to split such a line correctly. Which approach is best?

A.Manually iterate over characters and track quote state.
B.Use str.split(',') after removing all quotes.
C.Use csv.reader([line]) to parse the line.
D.Use re.split(r',(?=(?:[^"]*"[^"]*")*[^"]*$)', line)
AnswerC

csv.reader([line]) is the correct approach because csv.reader is a full CSV parser implementing the quoting rules (RFC 4180 and the dialect parameters such as quotechar, doublequote, escapechar, and delimiter). By passing [line] — a one-element list, not the raw string — you fulfill csv.reader's expectation of an iterable of lines, and it returns a single parsed row as a list of fields. It correctly handles commas embedded inside quoted fields, escaped double quotes ("" inside a quoted field), and quoted fields with surrounding whitespace, all without manual parsing or brittle regular expressions. This is exactly the kind of robust, tested behavior the Python standard library provides for CSV data.

Why this answer

Python's `csv.reader` is specifically designed to handle CSV parsing according to RFC 4180, including quoted fields that contain commas, newlines, and embedded quotes. It automatically manages quote state and field boundaries, making it the most robust and Pythonic solution for this task.

Exam trap

Python Institute often tests the misconception that regex or manual string splitting is sufficient for CSV parsing, when in fact the `csv` module is the standard library solution that correctly handles all edge cases defined by the CSV format specification.

How to eliminate wrong answers

Option A is wrong because manually iterating over characters and tracking quote state is error-prone, reinvents the wheel, and violates the principle of using built-in libraries for standard formats. Option B is wrong because removing all quotes before splitting destroys the structure of quoted fields (e.g., 'Doe, Jr.' becomes 'Doe, Jr.' and then splits incorrectly on the comma inside). Option D is wrong because the regex pattern, while attempting to match commas outside quotes, is fragile and fails on edge cases like escaped quotes, uneven quote counts, or empty quoted fields; it also has poor performance on large files.

19
MCQhard

What is the output of the following code? try: exec('1/0') except: print('error') else: print('no error') finally: print('done')

A.error done
B.done error
C.error
D.done
AnswerA

Correct order.

Why this answer

The `exec('1/0')` raises a `ZeroDivisionError`, which is caught by the bare `except:` clause, printing 'error'. The `else` clause is skipped because an exception occurred, but the `finally` clause always executes, printing 'done'. Thus the output is 'error' followed by 'done'.

Exam trap

Python Institute often tests the order of execution in exception handling, specifically that `finally` always runs after `except` (not before), and that `else` is skipped when an exception occurs, causing candidates to misorder the output or forget the `finally` block.

How to eliminate wrong answers

Option B is wrong because it suggests 'done' prints before 'error', but the `except` block runs before the `finally` block, so the order is 'error' then 'done'. Option C is wrong because it omits the `finally` block output entirely, but `finally` always executes regardless of exceptions. Option D is wrong because it omits the 'error' output, but the exception is caught and 'error' is printed.

20
MCQeasy

Which of the following is the BEST practice for building a large string by concatenating many smaller strings in Python?

A.result = ''.join(parts)
B.result = sum(parts, '')
C.result = str.concat(*parts)
D.result = ''; for part in parts: result += part
AnswerA

''.join(parts) is the canonical and most efficient way to combine a sequence of strings in Python. It allocates exactly one new string object, iterates over parts once, and copies each part's characters into a pre-sized buffer, achieving O(n) time and minimal memory overhead. This method clearly communicates intent and avoids the quadratic behavior of repeated concatenation.

Why this answer

`''.join(parts)` is the most efficient way to concatenate a large number of strings in Python. It allocates memory once for the final string by iterating over the list and copying each part into the result buffer, avoiding the O(n²) time complexity of repeated concatenation in a loop.

Exam trap

Python Institute often tests the misconception that `+=` is acceptable for all string building, or that `sum` or non-existent methods like `str.concat` are valid, when in fact `''.join()` is the only efficient and correct approach for large concatenations.

How to eliminate wrong answers

Option B is wrong because `sum(parts, '')` is not intended for string concatenation; it performs addition with a start value of an empty string, which raises a TypeError because `sum` expects numeric types by default and does not support string concatenation. Option C is wrong because `str.concat(*parts)` is not a valid Python built-in method; there is no `str.concat` function, and this would raise an AttributeError. Option D is wrong because using `result += part` in a loop creates a new string object for each iteration, leading to O(n²) time complexity due to repeated memory allocation and copying, making it inefficient for large numbers of parts.

21
MCQhard

Refer to the exhibit. What is printed?

A.3\n6
B.3\nError
C.Error\n3
D.3\n3
AnswerD

On the first invocation, the function computes and returns 3, and the cache stores that result keyed by the argument. The second invocation sees the argument already in the cache and immediately returns the stored value 3 without re-entering the function. This behavior is exactly what caching decorators like `functools.lru_cache` provide, making the output consistent.

Why this answer

The code defines a class `A` with a class attribute `x = 3`. Inside `__init__`, the first `print(self.x)` accesses the class attribute (since no instance attribute exists yet), printing `3`. The statement `self.x += 1` is equivalent to `self.x = self.x + 1`; it reads the class attribute for the right-hand side, evaluates to `4`, and then creates a new instance attribute `x` with value `4`, shadowing the class attribute.

The second `print(A.x)` explicitly accesses the class attribute, which remains `3`. Hence the output is `3` and `3` on separate lines.

Exam trap

Python Institute often tests the subtle difference between class attributes and instance attributes, specifically that `self.x += 1` creates a new instance attribute rather than modifying the class attribute, leading candidates to mistakenly think the class attribute itself is incremented.

How to eliminate wrong answers

Option A is wrong because it suggests the second value is 6, which would require the increment to be applied twice or a different operation. Option B is wrong because it suggests an error occurs after printing 3, but no error occurs; the code runs successfully. Option C is wrong because it suggests an error is printed first, but the first print statement executes without error, printing 3.

22
MCQmedium

Refer to the exhibit. Which of the following fixes the error?

A.print('Hello' + '5')
B.print('Hello' + str(5))
C.Both A and B
D.print('Hello' * 5)
AnswerC

Both A and B produce the intended output without error. Option A uses direct string concatenation with a string literal, while option B uses explicit conversion of the integer to a string. Neither causes a TypeError, so both fix the error described in the exhibit.

Why this answer

Both A and B produce the string 'Hello5' without error. In Python, the + operator concatenates strings, so 'Hello' + '5' works. Option B converts the integer 5 to a string using str() before concatenation, which also works.

Option D uses the * operator to repeat the string 'Hello' five times, producing 'HelloHelloHelloHelloHello', which is a valid operation but does not fix the error described in the exhibit (likely a TypeError from trying to concatenate a string and an integer).

Exam trap

Python Institute often tests the distinction between implicit type conversion (which Python does not do for string+int) and explicit conversion using str(), and candidates may forget that string repetition with * is valid but does not solve a concatenation error.

How to eliminate wrong answers

Option A is wrong because it is actually correct—it concatenates two strings without error, so it does fix the error. Option B is wrong because it is also correct—it converts the integer to a string before concatenation, fixing the error. Option D is wrong because while it is a valid Python expression, it repeats the string 'Hello' five times rather than concatenating it with 5, so it does not address the specific error of concatenating a string and an integer.

23
MCQhard

A developer notices that a custom package 'mypackage' is not being found when importing, even though it is installed in the site-packages directory. The developer suspects a conflict with another package of the same name. Which command should the developer run to diagnose the location from which Python is importing the package?

A.print(mypackage)
B.print(__file__)
C.print(mypackage.__file__)
D.import os; print(os.getcwd())
AnswerC

For an imported module, the `__file__` attribute stores the filesystem path of the source file from which the module was loaded. When `mypackage` is a package, its `__file__` points to the package's `__init__.py` file, which is exactly the location of the package on disk in most ordinary cases. This is the standard, programmatic way to determine where a package or module resides, making this option correct.

Why this answer

`mypackage.__file__` returns the filesystem path from which the module was loaded, allowing the developer to see exactly which `mypackage` Python is using. This directly reveals if the wrong package (e.g., from a different location or a conflicting installation) is being imported instead of the intended one.

Exam trap

The trap here is that candidates often confuse `__file__` (which gives the current script's path) with `module.__file__` (which gives the imported module's path), or they assume `print(mypackage)` will show the path directly, when in fact it may only show a module representation without the full path in all contexts.

How to eliminate wrong answers

Option A is wrong because `print(mypackage)` will print a string representation of the module object (e.g., `<module 'mypackage' from '/path/to/...'>`), but it does not reliably show the file path in all Python versions or environments, and it is not the standard diagnostic command. Option B is wrong because `print(__file__)` prints the path of the current script, not the imported package, so it provides no information about where `mypackage` is located. Option D is wrong because `print(os.getcwd())` prints the current working directory, which is unrelated to the import resolution path for installed packages.

24
MCQmedium

A developer has a module 'config.py' with the following content: # config.py import os DATABASE_URL = os.getenv('DATABASE_URL', 'localhost') Another module 'app.py' imports config and uses DATABASE_URL. During testing, the environment variable is set correctly, but the import still uses the default value 'localhost'. What is the most likely reason?

A.The import statement in app.py is placed inside a function, so it is not executed.
B.The module was imported using 'from config import DATABASE_URL' which creates a separate copy.
C.The environment variable is only read when the function is called, not at import time.
D.Python caches modules; config.py was imported earlier without the environment variable, and the cached version is reused.
AnswerD

Python records every imported module in sys.modules, and a later import of the same module simply fetches that cached object instead of re-executing the file. If config.py was imported earlier in the same interpreter session before the environment variable was set, its module-level code—including the os.getenv call—has already run and stored a stale default. All subsequent imports, whether 'import config' or 'from config import DATABASE_URL', see that cached module and its fixed value, so the code is not re-executed.

Why this answer

Python caches imported modules in `sys.modules`. If `config.py` was imported earlier in the test session (e.g., during test discovery or another import) before the environment variable `DATABASE_URL` was set, the cached module would retain the default value `'localhost'`. Subsequent imports, even after setting the environment variable, reuse the cached module, so `os.getenv('DATABASE_URL', 'localhost')` is not re-evaluated.

Exam trap

Python Institute often tests the misconception that `from module import name` creates an independent copy, when in fact it only binds a reference to the same object, and the real issue is Python's module caching and the timing of environment variable reads.

How to eliminate wrong answers

Option A is wrong because placing an import inside a function does not prevent its execution; the import is executed when the function is called, and the module is still cached. Option B is wrong because `from config import DATABASE_URL` creates a local name binding to the same object, not a separate copy; the issue is about the value at import time, not copying. Option C is wrong because `os.getenv` is called at import time (when the module is first loaded), not when a function is called; the environment variable is read once during module initialization.

25
MCQmedium

A data analyst is cleaning a CSV file. They have a string variable containing a row of data: 'John,Doe,30,New York'. They need to extract the last name 'Doe' using string methods. The analyst writes: name = row.split(',')[1]. However, they are concerned about performance because the file contains millions of rows. They want to use a more efficient method that extracts the substring without creating a full list. Which approach should the analyst use?

A.Use split(',', 2) and take the second element
B.Use partition(',') and get the third element
C.Use rsplit(',', 1) and take the first part
D.Use string slicing after finding the comma positions: start = row.find(',')+1; end = row.find(',', start); name = row[start:end]
AnswerD

This technique directly extracts the substring between the first and second commas using index arithmetic. `row.find(',')` returns the index of the first comma, so adding 1 gives the character position immediately after it; then `row.find(',', start)` scans forward from that position to locate the second comma. Slicing `row[start:end]` copies only the needed characters and avoids creating a list of all fields, making it both memory-efficient and precise for this fixed-position CSV pattern.

Why this answer

It avoids creating a full list of all fields by using `find()` to locate the comma positions and then slicing the substring directly. This approach is more memory-efficient for millions of rows, as it only extracts the required portion without splitting the entire string into a list.

Exam trap

A common trap in PCAP is thinking that any split() variant is always the best approach, ignoring the memory overhead of list creation in performance-critical scenarios.

How to eliminate wrong answers

Option A is wrong because `split(',', 2)` still creates a list of up to 3 elements, which is more efficient than a full split but still allocates a list object for each row. Option B is wrong because `partition(',')` returns a tuple of three strings (before, separator, after), but the third element is the remainder after the first comma, not the last name; to get 'Doe', you would need the second element (the part between the first and second commas), which is not directly provided. Option C is wrong because `rsplit(',', 1)` splits from the right, returning a list of two elements where the first part is everything before the last comma, which would be 'John,Doe,30' — not the last name.

26
MCQmedium

Given that MyClass defines __private_attr in __init__, why does this error occur?

A.The attribute name is mangled to _MyClass__private_attr.
B.The attribute was not defined in __init__.
C.Private attributes cannot be accessed outside the class.
D.The attribute is a class attribute not an instance attribute.
AnswerA

Python applies name mangling to any identifier of the form __spam (with at most one trailing underscore) that occurs inside a class definition, rewriting it to _ClassName__spam. So self.__private_attr declared in __init__ is stored as self._MyClass__private_attr by the compiler. From outside the class, you must use that mangled name; accessing __private_attr directly will fail because no such attribute exists under that literal name.

Why this answer

Python uses name mangling for attributes with double underscores (__) to avoid accidental overriding in subclasses. When you define __private_attr inside __init__, Python internally renames it to _MyClass__private_attr. Attempting to access obj.__private_attr from outside the class fails because that mangled name is not recognized, leading to an AttributeError.

Exam trap

The Python Institute often tests the misconception that double underscores create truly private attributes, leading candidates to choose 'Private attributes cannot be accessed outside the class' when the real issue is name mangling and the attribute still being accessible via the mangled name.

How to eliminate wrong answers

Option B is wrong because the attribute __private_attr is indeed defined in __init__; the error is not due to missing definition but due to name mangling. Option C is wrong because Python does not enforce true private access; the attribute can still be accessed using the mangled name _MyClass__private_attr, so the statement 'cannot be accessed outside the class' is technically false. Option D is wrong because __private_attr is assigned to self inside __init__, making it an instance attribute, not a class attribute.

27
Multi-Selectmedium

Which TWO of the following are valid ways to import a function 'foo' from a module 'bar' that is located in a package 'mypackage'?

Select 2 answers
A.from mypackage import bar.foo
B.from mypackage.bar import foo
C.import mypackage.bar; then use bar.foo
D.from . import bar.foo
E.import mypackage.bar.foo
AnswersB, C

Correct absolute import.

Why this answer

The syntax `from mypackage.bar import foo` directly imports the function `foo` from the module `bar` within the package `mypackage`. This is the standard Python import statement for importing a specific attribute from a submodule.

Exam trap

Python Institute often tests the distinction between importing a module versus importing an attribute from a module, and the trap here is that candidates mistakenly think `from mypackage import bar.foo` is valid because they confuse it with the valid `from mypackage.bar import foo` syntax.

28
MCQmedium

A developer writes code to display a floating-point number with exactly two decimal places. Which f-string expression is correct for value = 3.14159?

A.f"{value:0.2}"
B.f"{value:.2f}"
C.f"{value:%2f}"
D.f"{value:2f}"
AnswerB

This is the correct f-string format specifier: the dot ('.') introduces a precision field, '2' is the number of digits to display after the decimal point, and 'f' selects fixed-point notation. For example, f"{3.14159:.2f}" evaluates to '3.14', and Python automatically rounds the value to the requested precision. This matches the requirement to display exactly two decimal places, making it the only valid option among the choices.

Why this answer

The format specifier `.2f` in an f-string explicitly instructs Python to format the floating-point number with exactly two digits after the decimal point. The `f` type ensures fixed-point notation, and the precision `.2` controls the number of decimal places. This is the standard way to achieve two-decimal-place output for a float in Python.

Exam trap

Python exams often test the distinction between width and precision in format specifiers, trapping candidates who confuse `0.2` (width.precision without type) with `.2f` (precision with float type), or who mistakenly use `%` syntax from older Python formatting styles.

How to eliminate wrong answers

Option A is wrong because `0.2` is a width-and-precision specifier without a type code; it pads the number to a total width of 2 characters (including the decimal point) but does not guarantee two decimal places, and for `3.14159` it would produce `3.14159` (no truncation) or cause unexpected behavior. Option C is wrong because `%2f` is not a valid format specifier; the `%` character is used for old-style `%` formatting, not f-string syntax, and `2f` is misinterpreted. Option D is wrong because `2f` lacks a decimal point before the precision; it sets a minimum field width of 2 but does not specify decimal places, so it would output the full float without truncation (e.g., `3.14159`).

29
MCQhard

You are a developer for a data science team. The team uses a shared module 'utilities' located at /team/shared/utilities.py. This module is not part of any package, and they want to import it from various project scripts without copying the file. Some projects are in /home/user/proj_A/ and others in /var/data/proj_B/. Currently, each script manually adds /team/shared/ to sys.path using sys.path.insert(0, '/team/shared/'). This works but is repetitive. The team wants a cleaner solution that also works when the script is run from different working directories. They consider creating a package 'utilities' by adding an __init__.py to the directory and using relative imports. However, the module currently uses absolute imports for some external libraries. What is the best course of action to allow clean imports of utilities from any location while minimizing changes to the module itself?

A.Create an empty __init__.py in /team/shared/ to make it a namespace package.
B.Set the PYTHONPATH environment variable to include /team/shared/ in the shell profile.
C.Place a .pth file in the site-packages directory that points to /team/shared/.
D.Convert utilities.py into a package by adding __init__.py and using relative imports inside.
AnswerB

Setting PYTHONPATH in the shell profile prepends /team/shared/ to the module search path (sys.path) for every Python process spawned from that shell. This allows `import utilities` to resolve cleanly without altering the module's internals, placing the shared directory in a well-known environment variable rather than hard-coding it into each script. It is minimal, reversible, and does not touch the Python installation or require packaging changes.

Why this answer

Setting the PYTHONPATH environment variable to include /team/shared/ is the best solution because it automatically adds that directory to the module search path for all Python scripts without modifying the module itself or requiring repetitive code. This approach works regardless of the current working directory and preserves the module's existing absolute imports. Other options either do not add the directory to the search path, require modifying the module, or are more complex to implement.

Exam trap

A common mistake is to think that adding an __init__.py file makes a directory importable from anywhere. In reality, __init__.py only marks a directory as a Python package, but the directory must already be in the module search path (sys.path) to be imported. Without PYTHONPATH or sys.path manipulation, the package is not discoverable from arbitrary locations.

How to eliminate wrong answers

Option A is wrong because creating an empty __init__.py in /team/shared/ would make it a regular package, not a namespace package, and would not automatically add the directory to the module search path; scripts would still need to modify sys.path or rely on PYTHONPATH. Option C is wrong because placing a .pth file in site-packages adds the directory to sys.path only for the specific Python installation where site-packages resides, which may not be portable across different environments or projects, and it requires administrator privileges. Option D is wrong because converting utilities.py into a package by adding __init__.py and using relative imports would require rewriting the module to use relative imports, which contradicts the goal of minimizing changes and could break existing absolute imports for external libraries.

30
MCQeasy

A function receives a file path like '/home/user/docs/file.txt' and needs to return the path without the file extension, e.g., '/home/user/docs/file'. Which code reliably removes only the last dot extension, even if the directory names contain dots?

A.path.split('.')[0]
B.path.rsplit('.', 1)[0]
C.path.replace('.', '', 1)
D.path[:path.find('.')]
AnswerB

path.rsplit('.', 1) splits from the right side using a maxsplit of 1, so it stops after encountering the last dot in the string, and [0] gives the substring before that final dot. This correctly removes only the extension from a path such as '/home/user/file.txt', producing '/home/user/file' while preserving any dots in the directory path, as with '/home/user.name/file.txt' -> '/home/user.name/file'. It is the string-method idiom for stripping a trailing extension, although os.path.splitext is the more robust alternative in practice.

Why this answer

`rsplit('.', 1)` splits the string from the right, limiting the split to exactly one occurrence, which isolates the file extension (the part after the last dot) and returns everything before it. This reliably removes only the last dot extension, even if directory names contain dots, because it targets the final dot in the path.

Exam trap

The Python PCAP exam often tests the distinction between `split` and `rsplit` with the maxsplit parameter, and the trap here is that candidates mistakenly use `split('.')[0]` or `path[:path.find('.')]`, which fail when directory names contain dots because they target the first dot instead of the last.

How to eliminate wrong answers

Option A is wrong because `split('.')` splits on every dot in the path, returning a list of all segments; taking index `[0]` only gives the part before the first dot, which would incorrectly truncate the path at the first dot (e.g., '/home/user/docs' from '/home/user/docs/file.txt' becomes '/home/user/docs' instead of '/home/user/docs/file'). Option C is wrong because `replace('.', '', 1)` replaces only the first occurrence of a dot, which would remove the dot in a directory name (e.g., 'docs' in '/home/user/docs/file.txt' becomes '/home/user/docsfile.txt') rather than the extension dot. Option D is wrong because `path[:path.find('.')]` finds the index of the first dot and slices up to it, which again truncates at the first dot and fails if directory names contain dots (e.g., '/home/user/docs/file.txt' becomes '/home/user/docs').

31
MCQhard

You are developing a high-performance logging module that must handle thousands of log entries per second. Each entry is built by concatenating a timestamp, level, and message. Currently, your code uses a loop that repeatedly appends to a string using the += operator. This results in high memory usage and sluggish performance because each concatenation creates a new string object. The module must run on systems with limited memory and cannot rely on external libraries. Which course of action would best resolve the performance issue while maintaining readability and standard library compliance?

A.Collect the string parts in a list and use str.join() to combine them at the end.
B.Use string formatting (f-strings or format) within the loop to build the log entry.
C.Write the log entries directly to a file using file.write() in the loop.
D.Continue using += but preallocate a large string buffer using array.array or io.StringIO to reduce reallocation.
AnswerA

Accumulating fragments in a list and calling str.join() only at the end is efficient because Python can first calculate the total length of the combined result, allocate a single string buffer exactly once, and then copy each fragment into place. This avoids the O(n²) copying behavior of repeated concatenation, where each += operation allocates a new string and copies all previous content. For logging modules that assemble many small parts per entry, this is the recommended Pythonic pattern.

Why this answer

Collecting string parts in a list and using str.join() avoids repeated string concatenation, which creates a new string object for each += operation. This approach reduces memory allocation overhead and improves performance, especially under high throughput, while remaining fully compliant with standard library constraints.

Exam trap

Python Institute often tests the misconception that string formatting (f-strings) or incremental I/O (file.write) avoids the immutability penalty, when in fact they still create new string objects or introduce I/O latency, respectively.

How to eliminate wrong answers

Option B is wrong because using f-strings or format() inside the loop still creates a new string object per iteration, incurring the same memory and performance penalty as +=. Option C is wrong because writing directly to a file in the loop introduces I/O overhead for each log entry, which is slower than batching writes and may cause excessive disk writes under high load. Option D is wrong because preallocating a buffer with array.array or io.StringIO does not eliminate the fundamental issue of repeated string concatenation; io.StringIO is designed for incremental building but still involves internal reallocation, and array.array is not intended for string concatenation, leading to complexity and potential type errors.

32
Multi-Selecteasy

Which TWO statements about the 'from package import *' statement are correct?

Select 2 answers
A.It imports the package itself as a module.
B.Without __all__, it imports all public names from the package's __init__.py and all submodules.
C.It imports all submodules of the package by default.
D.If __all__ is defined in __init__.py, only the names in __all__ are imported.
E.The behavior can be customized by defining the __all__ list in __init__.py.
AnswersD, E

When `__all__` is defined in the package's `__init__.py`, `from package import *` imports exactly the names contained in that list. Any name not present in `__all__` is ignored, even if it is a public variable, function, or a submodule also defined in the package. This explicit list overrides the default underscore-filtering behavior and gives the package author precise control over the subset of the package's API that is exposed to star imports.

Why this answer

When `__all__` is defined in a package's `__init__.py`, the `from package import *` statement imports only the names listed in that `__all__` list. This is the explicit mechanism Python provides to control the public API of a package when using the wildcard import syntax.

Exam trap

The PCAP exam often tests the misconception that `from package import *` automatically imports all submodules, when in fact it only imports names from the package's `__init__.py` (or those listed in `__all__`), and submodules must be explicitly imported or listed to be included.

33
MCQmedium

Refer to the exhibit. A developer is writing a script to read this JSON configuration file. The script should write the logging configuration to a separate file called 'logging.conf'. Which file mode should be used to create the file if it doesn't exist, and overwrite it if it does?

A.'x'
B.'r+'
C.'a'
D.'w'
AnswerD

Mode 'w' opens the file for writing, creating the file if it does not exist and truncating it to zero length if it does, thereby discarding all previous contents. This exactly matches the need to write a complete, fresh set of data: any existing content is erased before the first write, ensuring that the final file contains only the current output with no stale data.

Why this answer

('w') is correct because the 'w' mode opens a file for writing, truncating it first if it exists, and creating it if it does not. This matches the requirement to overwrite an existing 'logging.conf' file or create a new one if absent.

Exam trap

Python Institute often tests the distinction between 'w' and 'x' modes, where candidates mistakenly choose 'x' thinking it creates a new file, forgetting that 'x' raises an error if the file already exists, thus failing the overwrite requirement.

How to eliminate wrong answers

Option A ('x') is wrong because 'x' is an exclusive creation mode that raises a FileExistsError if the file already exists, so it cannot overwrite an existing file. Option B ('r+') is wrong because 'r+' opens a file for reading and writing but does not create the file if it does not exist; it raises a FileNotFoundError. Option C ('a') is wrong because 'a' opens a file for appending, which does not overwrite existing content; it writes new data at the end of the file.

34
MCQmedium

Refer to the exhibit. What is printed?

A.' Alice is 025 years old.'
B.' Alice is 25 years old.'
C.'Alice is 25 years old.'
D.' Alice is 25 years old.'
AnswerA

Correct: name right-aligned, age zero-padded.

Why this answer

The Python code uses an f-string with the format specifier `:>10s}` for the name and `:03d}` for the age. The `>10s` right-aligns the string 'Alice' in a field of width 10, producing 5 leading spaces. The `03d` formats the integer 25 as a zero-padded three-digit string '025'.

The final output is `' Alice is 025 years old.'`.

Exam trap

Python Institute often tests the subtle distinction between string padding (spaces) and numeric zero-padding, and the fact that the `>` alignment specifier applies to strings while `0` padding applies only to numbers, causing candidates to overlook the leading spaces or the zero-padded age.

How to eliminate wrong answers

Option B is wrong because it omits the leading spaces (the `>10s` specifier right-aligns the name in a 10-character field, producing 5 spaces before 'Alice') and incorrectly shows the age as '25' instead of zero-padded '025'. Option C is wrong because it incorrectly places spaces after 'Alice' (the `>10s` specifier right-aligns, not left-aligns, so spaces appear before, not after). Option D is wrong because it shows the age as '25' without the leading zero required by the `03d` format specifier.

35
MCQhard

A Python script reads a file containing text with non-ASCII characters like 'é' and 'ü'. The script must encode the string as UTF-8 then decode it back. Which of the following correctly handles this without error?

A.s.decode('utf-8').encode('utf-8')
B.s.encode('ascii').decode('ascii')
C.s.encode('utf-8').decode('utf-8')
D.s.decode('utf-8').decode('utf-8')
AnswerC

This is the correct round-trip: s.encode('utf-8') serializes the Unicode string into a bytes object using UTF-8's variable-length encoding, and .decode('utf-8') deserializes those exact bytes back into the original str. Because UTF-8 can encode every Unicode code point, the transformation is lossless and the resulting string is equal to s. Unlike the wrong options, it respects the proper direction (str → bytes → str) and never applies decode to a str. This pattern is commonly used when passing text through byte-oriented APIs or verifying byte-level round-trippability.

Why this answer

It first encodes the string (which contains non-ASCII characters like 'é' and 'ü') into UTF-8 bytes using `.encode('utf-8')`, then decodes those bytes back into a string using `.decode('utf-8')`. This round-trip preserves all characters since UTF-8 can represent any Unicode code point, and the operations are applied in the correct order: a string is encoded to bytes, then bytes are decoded back to a string.

Exam trap

Python Institute often tests the distinction between string and bytes methods — the trap here is that candidates confuse `.encode()` and `.decode()`, thinking both can be called on strings, or they incorrectly assume ASCII can handle non-ASCII characters without error.

How to eliminate wrong answers

Option A is wrong because it attempts to decode a string (which is already a Unicode object) using `.decode('utf-8')`, which raises an `AttributeError` — decode is a method of bytes, not str. Option B is wrong because it encodes the string to ASCII, which will raise a `UnicodeEncodeError` for non-ASCII characters like 'é' and 'ü' since ASCII only supports code points 0–127. Option D is wrong because it calls `.decode()` twice on a string, which is invalid for the same reason as Option A — the first decode fails, and even if it were bytes, double decoding would produce garbage or an error.

36
MCQmedium

A developer creates classes `A`, `B(A)`, `C(A)`, and `D(B, C)`. When calling a method from `D` that is defined in `A`, which class's version is used according to Python's MRO?

A.The method from B, because it is the first parent.
B.The method from C, because it appears after B.
C.The method from A, but only if B and C do not override it.
D.The method from A, found through B then C.
AnswerD

The C3 linearization algorithm for class D(B, C), where B and C both inherit from A, produces the MRO D -> B -> C -> A. The attribute lookup follows this exact sequence: it checks D, then B, then C, and finally A; since neither B nor C defines the method, the first defining class encountered is A. Thus the method is found on A after the traversal through B and C.

Why this answer

Python's Method Resolution Order (MRO) for class `D(B, C)` follows the C3 linearization algorithm, which ensures a depth-first left-to-right search while preserving monotonicity. For `D(B, C)`, the MRO is `D -> B -> C -> A`, so a method defined in `A` that is not overridden in `B` or `C` will be found via `B` first, then `C`, and finally `A`. Option D correctly states that the method from `A` is used, found through `B` then `C`, which matches the actual resolution path.

Exam trap

Python Institute often tests the misconception that Python's MRO simply searches the first parent and its ancestors before moving to the next parent (depth-first left-to-right), but the actual C3 linearization can produce a different order, especially in diamond inheritance, and candidates may incorrectly assume the method from `A` is found directly without considering the intermediate classes in the MRO.

How to eliminate wrong answers

Option A is wrong because it assumes the first parent's version is always used, but Python's MRO does not simply stop at the first parent; it uses C3 linearization to consider all ancestors in a specific order, and if `B` does not override the method, the search continues to `C` and then `A`. Option B is wrong because it incorrectly suggests that `C`'s version is used because it appears after `B` in the class definition, but the MRO for `D(B, C)` is `D -> B -> C -> A`, so `B` is checked before `C`, and the method from `A` is only reached if neither `B` nor `C` overrides it. Option C is wrong because it implies the method from `A` is used only if `B` and `C` do not override it, which is true, but it omits the critical detail that the resolution path goes through `B` then `C` before reaching `A`, and the statement 'found through B then C' is essential to understanding MRO; the option as phrased is incomplete and misleading.

37
MCQhard

A user wants to ensure that a custom module 'mymod' located at '/home/user/custom' takes precedence over a standard library module with the same name. Which operation on sys.path should be performed?

A.sys.path.replace('/', '/home/user/custom')
B.sys.path.append('/home/user/custom')
C.sys.path.insert(0, '/home/user/custom')
D.sys.prefix = '/home/user/custom'
AnswerC

insert(0, '/home/user/custom') places the custom directory at the very beginning of sys.path, making it the first location searched by the import machinery. This guarantees that modules in that directory take precedence over identically named modules found later in the path, including the standard library and site-packages. It also avoids issues with the working directory or other early entries, which is why insert(0, ...) is the conventional idiom for local module overrides.

Why this answer

`sys.path.insert(0, '/home/user/custom')` adds the custom module's directory to the very beginning of the module search path. Python's import system scans `sys.path` in order, so placing the custom directory first ensures that `mymod` is found there before any standard library or site-packages directory that might contain a module with the same name.

Exam trap

Python Institute often tests the distinction between `insert(0, ...)` and `append(...)`, knowing that many candidates mistakenly think adding a path anywhere in `sys.path` will override standard modules, but only insertion at the beginning achieves that precedence.

How to eliminate wrong answers

Option A is wrong because `sys.path.replace('/', '/home/user/custom')` is not a valid method on a list; `replace` is a string method and would raise an AttributeError. Option B is wrong because `sys.path.append('/home/user/custom')` adds the directory to the end of the list, so the standard library module (which is typically found earlier in `sys.path`) would still take precedence. Option D is wrong because `sys.prefix` is a read-only attribute that points to the Python installation directory; assigning to it does not affect the module search path and would raise an AttributeError or be ignored.

38
MCQhard

A team is using f-strings to format a report. They have a variable `value = 0.123456789` and want to display it with exactly 3 significant digits. They write `f"{value:.3g}"`. The output is '0.123'. They expected '0.123'. Is the output correct? If not, what change would produce '0.123'?

A.Use `f"{value:.3s}"`
B.Use `f"{value:.3f}"`
C.Use `f"{value:.3e}"`
D.The output is correct as is.
AnswerD

Correct—the format spec already in use (`.3g`) rounds to three significant digits and picks fixed-point notation for this magnitude. For `0.123456`, the three significant digits are 1, 2, and 3, and because the adjusted exponent is within the `g` threshold, it prints as `0.123` without an exponent. The output is exactly what the report requires, so no alternative specifier is needed.

Why this answer

The format specifier `.3g` in an f-string instructs Python to format the number with 3 significant digits using general format. For `0.123456789`, the first three significant digits are '123', and the general format automatically switches to fixed-point notation when the exponent is small, producing '0.123' exactly as expected.

Exam trap

The trap here is that candidates confuse 'significant digits' (controlled by `g`) with 'decimal places' (controlled by `f`), leading them to incorrectly choose `.3f` when `.3g` is the correct specifier for significant digits.

How to eliminate wrong answers

Option A is wrong because `s` is not a valid format type for numeric values; it is used for strings and would raise a ValueError. Option B is wrong because `.3f` formats with exactly 3 digits after the decimal point, which would produce '0.123' only by coincidence for this value, but it is not the correct approach for significant digits; for a value like 0.0012345, `.3f` would give '0.001' (only 1 significant digit), not 3. Option C is wrong because `.3e` forces scientific notation with 3 digits after the decimal point, producing '1.235e-01' (rounded), not '0.123'.

39
MCQmedium

A network engineer processes a configuration file containing MAC addresses in the format 'aa:bb:cc:dd:ee:ff'. They need to convert each MAC address into a 6-byte bytes object for use in packet crafting. The current code is: mac_bytes = bytes([int(x, 16) for x in mac_str.split(':')]). This works correctly, but they need to process thousands of MAC addresses and want to optimize performance. They also need to handle invalid MAC addresses (e.g., non-hex characters) without crashing. Which of the following approaches is the most efficient and robust?

A.Use the same list comprehension but add a try-except block for ValueError
B.Use bytes.fromhex(mac_str.replace(':', ''))
C.Use struct.pack('BBBBBB', *[int(x,16) for x in mac_str.split(':')])
D.Use a for loop to parse each pair and build a bytearray
AnswerB

bytes.fromhex() is a built-in method implemented in C that parses a hex string directly into a bytes object, making it the fastest and most idiomatic choice. Removing the colons with .replace(':', '') yields a 12-character hex string, which fromhex converts to exactly six bytes. It also performs validation in the C layer: non-hex characters or odd-length strings raise ValueError, giving the same error behavior as a manual parse but without Python-level iteration.

Why this answer

`bytes.fromhex()` is implemented in C, making it significantly faster than a Python-level list comprehension for thousands of conversions. It also inherently validates that the input contains only hexadecimal characters (and colons, which are ignored after removal), raising a `ValueError` for invalid input, which can be caught for robustness. This approach avoids the overhead of splitting, iterating, and calling `int()` for each octet.

Exam trap

The PCAP exam often tests the misconception that a list comprehension or `struct.pack` is the most efficient approach, when in reality Python's built-in `bytes.fromhex()` leverages C-level optimization for both speed and validation.

How to eliminate wrong answers

Option A is wrong because while it adds error handling, it still uses the slower list comprehension with `int(x, 16)` for each octet, which involves Python-level iteration and function calls, making it less efficient than the C-level `bytes.fromhex()`. Option C is wrong because `struct.pack()` adds unnecessary overhead by requiring the list comprehension to produce the integers first, then packing them into bytes; it is neither the most efficient nor the most direct method. Option D is wrong because a manual for loop with `bytearray` is the slowest approach, as it involves Python-level iteration, multiple function calls, and incremental appending, which is far less efficient than the single C-level call in Option B.

40
MCQmedium

A developer generates a report where numbers must be right-aligned in a 10-character column using f-strings: f'{value:>10}'. However, some values may be None, causing a TypeError. Which is the most robust way to handle None values without affecting other falsy values like 0?

A.Use str.format() with a conditional for the format spec
B.f'{value or "N/A":>10}'
C.f'{value if value is not None else "N/A":>10}'
D.Wrap the f-string in a try-except block
AnswerC

This option uses a conditional expression that explicitly tests identity with `is not None`, meaning only `None` triggers the fallback while preserving all other values, including 0 and empty strings. The f-string then applies the `:>10` format spec to the selected result, right-aligning either 'N/A' or the numeric value in a 10-character field. It is the only choice that correctly distinguishes a missing sentinel from legitimate falsy data.

Why this answer

It uses an explicit identity check (`value is not None`) to distinguish `None` from other falsy values like `0` or empty strings. This ensures that `0` is still right-aligned as a number, while `None` is replaced with the string `"N/A"` before formatting. The f-string then applies the `>10` alignment specifier to the resulting value.

Exam trap

The PCAP exam often tests the distinction between identity checks (`is None`) and truthiness checks (`or`, `if value`) to catch candidates who assume all falsy values should be treated equally, especially when `0` is a valid numeric value that must be preserved.

How to eliminate wrong answers

Option A is wrong because `str.format()` with a conditional for the format spec does not inherently handle `None` values; it would still raise a `TypeError` when trying to format `None` unless the conditional also replaces the value itself. Option B is wrong because `value or "N/A"` treats `0` (a falsy number) as `None`, incorrectly replacing it with `"N/A"` instead of preserving it for right-alignment. Option D is wrong because wrapping the f-string in a `try-except` block is a reactive approach that catches the `TypeError` at runtime, but it is less robust and less readable than a proactive conditional check; it also requires additional logic to decide what to display on exception.

41
MCQeasy

Which of the following statements about the `finally` block is true?

A.It executes only if no exception is raised.
B.It does not execute if a return statement is in try block.
C.It executes only if an exception is raised.
D.It always executes, regardless of exceptions.
AnswerD

Finally is guaranteed to run.

Why this answer

The `finally` block in Python is designed to always execute after the `try` and `except` blocks, regardless of whether an exception was raised or not. This includes cases where a `return`, `break`, or `continue` statement is executed in the `try` block, or even if an unhandled exception occurs. The `finally` block is guaranteed to run before the function returns or the exception propagates, ensuring cleanup actions like closing files or releasing resources.

Exam trap

Python Institute often tests the misconception that a `return` statement in the `try` block prevents the `finally` block from executing, but in Python, the `finally` block always runs before the function returns, making this a common trap for candidates who confuse Python's behavior with that of other languages.

How to eliminate wrong answers

Option A is wrong because the `finally` block executes regardless of whether an exception is raised, not only when no exception occurs. Option B is wrong because the `finally` block does execute even if a `return` statement is in the `try` block; the `finally` block runs before the function actually returns. Option C is wrong because the `finally` block executes regardless of whether an exception is raised, not only when an exception occurs.

42
MCQeasy

A developer wants to create a class that logs every attribute access on an instance. Which special method should they override?

A.`__getattr__`
B.`__getattribute__`
C.`__setattr__`
D.`__delattr__`
AnswerB

This method is invoked for every attribute access, making it suitable for logging.

Why this answer

`__getattribute__` is the special method that is called unconditionally for every attribute access on an instance, making it the appropriate choice for logging all attribute accesses. Overriding this method allows the developer to intercept and log each access before the attribute is retrieved, whereas `__getattr__` is only invoked when the attribute is not found via normal lookup.

Exam trap

The trap here is that candidates confuse `__getattr__` (called only on missing attributes) with `__getattribute__` (called on every access), and Python Institute often tests this distinction by presenting a scenario requiring unconditional interception.

How to eliminate wrong answers

Option A is wrong because `__getattr__` is only called when an attribute is not found through the normal lookup mechanism (i.e., when `__getattribute__` raises an AttributeError), so it would not log every attribute access, only failed ones. Option C is wrong because `__setattr__` is called on attribute assignment, not access, so it cannot log reads. Option D is wrong because `__delattr__` is called on attribute deletion, not access, and is irrelevant to logging accesses.

43
MCQeasy

A class defines an __init__ method that takes optional arguments. What is the correct way to provide default values?

A.Use class variables to store defaults.
B.Use default parameter values in the __init__ signature.
C.Override __new__ to set default values.
D.Use a separate setter method called after instantiation.
AnswerB

Defining default parameter values in the __init__ signature is the canonical Python idiom for optional constructor arguments. These defaults are evaluated once at function definition time, but for immutable types (like None, int, str) that is harmless because rebinding a parameter simply rebinds the local name. This approach requires no extra code, allows callers to omit the argument or pass it by keyword, and keeps all initialization logic inside the constructor.

Why this answer

Python's `__init__` method, like any other function, supports default parameter values in its signature. This is the idiomatic and simplest way to provide default values for instance attributes, as the defaults are evaluated at function definition time and assigned to the parameter when no argument is provided.

Exam trap

The PCAP exam often tests the mutable default argument pitfall — candidates may incorrectly think that using a mutable default (like `[]` or `{}`) is safe, or they may confuse class variables with instance defaults, leading them to choose option A.

How to eliminate wrong answers

Option A is wrong because class variables are shared across all instances; mutating a default value stored as a class variable (e.g., a list) would affect all instances, which is not the intended behavior for per-instance defaults. Option C is wrong because overriding `__new__` is unnecessary and overly complex for setting default values; `__new__` is responsible for creating the instance, not for initializing attributes, and using it for defaults would be non-idiomatic and error-prone. Option D is wrong because relying on a separate setter method called after instantiation forces the caller to remember to invoke it, breaking the encapsulation and convenience that `__init__` provides; it also does not constitute a default value mechanism within the constructor itself.

44
Multi-Selecthard

Which THREE factors influence Python's module search path (sys.path)?

Select 3 answers
A.The HOME environment variable
B.The current working directory at runtime
C.The site-packages directory where pip installs packages
D.The directory containing the script being executed
E.The PYTHONPATH environment variable
AnswersC, D, E

Appended when site module is processed.

Why this answer

The site-packages directory is automatically included in sys.path by the site module during Python's initialization. This directory is the default location where pip installs third-party packages, making them importable without manual path manipulation.

Exam trap

Python Institute often tests the distinction between the current working directory at runtime and the directory containing the script being executed, leading candidates to incorrectly assume the working directory is always searched for modules.

45
MCQhard

Which of the following is a correct use of the @property decorator to create a getter and setter for an attribute named 'score' that ensures score stays between 0 and 100?

A.@property def _score(self): return self.score @_score.setter def _score(self, value): self.score = value
B.@property def score(self): return self.score @score.setter def score(self, value): self.score = value
C.def get_score(self): return self._score def set_score(self, value): self._score = value score = property(get_score, set_score)
D.@property def score(self): return self._score @score.setter def score(self, value): if 0 <= value <= 100: self._score = value
AnswerD

This is the canonical property pattern: the getter returns the private `_score` attribute, and the setter validates the incoming `value` before assigning it to `_score`. By using the backing field rather than the public property name, the code avoids recursion and gives the property exclusive control over reads and writes. When the validation condition fails, the setter silently refuses to update, keeping `_score` unchanged and enforcing the 0–100 range.

Why this answer

It uses the @property decorator to define a getter method that returns the private attribute `self._score`, and a setter method that validates the new value is between 0 and 100 before assigning it to `self._score`. This ensures encapsulation and data validation, which is the intended use of properties in Python.

Exam trap

The PCAP exam often tests the distinction between using the property name itself (causing recursion) versus a private backing attribute, and the requirement that the setter must include validation logic to satisfy constraints like range checks.

How to eliminate wrong answers

Option A is wrong because it uses `self.score` inside the getter and setter, which would cause infinite recursion (the getter calls itself) and does not store the value in a private attribute. Option B is wrong for the same reason: the getter returns `self.score`, which calls the getter again, leading to recursion; also the setter assigns to `self.score`, causing infinite recursion. Option C is wrong because it uses the traditional `property()` function with getter and setter methods, which is valid Python but does not use the @property decorator as required by the question; it also lacks validation logic to ensure the score stays between 0 and 100.

46
MCQmedium

A class has both `@classmethod` and `@staticmethod` decorators. What is a key difference between them?

A.A classmethod cannot be called on an instance.
B.A classmethod receives the class as first argument.
C.A staticmethod must be called from the class only.
D.A classmethod cannot access class variables.
AnswerB

That's the defining difference.

Why this answer

The key difference is that a `@classmethod` receives the class itself as the first implicit argument (conventionally named `cls`), allowing it to access or modify class-level state, while a `@staticmethod` receives no implicit first argument and behaves like a plain function, unable to access the class or instance. This makes option B correct because it accurately describes the distinguishing feature of a classmethod.

Exam trap

Python Institute often tests the misconception that classmethods cannot be called on instances, leading candidates to incorrectly select option A, when in fact they can be called on instances and still receive the class as the first argument.

How to eliminate wrong answers

Option A is wrong because a classmethod can be called on an instance; Python automatically passes the class of the instance as the first argument. Option C is wrong because a staticmethod can also be called on an instance, not only from the class; it simply does not receive any implicit first argument. Option D is wrong because a classmethod can access class variables via the `cls` parameter; it is specifically designed for that purpose.

47
MCQhard

A senior developer in a team argues that using try-except blocks is slower than checking conditions with if statements. They propose replacing all try blocks that handle file I/O errors with existence checks using os.path.exists before opening files. During a code review, you recall that Python's official documentation and best practices prefer EAFP (Easier to Ask for Forgiveness than Permission) over LBYL (Look Before You Leap) in many cases, especially in concurrent environments. The team's application is a multi-threaded web server that serves static files from a shared directory. Which is the strongest counterargument against the senior developer's proposal?

A.if statements are harder to read and maintain.
B.try-except can catch multiple exception types more cleanly.
C.try-except blocks have no performance cost at all.
D.LBYL leads to race conditions in concurrent code because the file's state can change between the check and the use.
AnswerD

This is the classic time-of-check-to-time-of-use (TOCTOU) race: in a multithreaded server, two threads can evaluate `os.path.exists(path)` at nearly the same instant, and then one thread may delete or replace the file before the other actually opens it. The check and the use are not atomic, so LBYL gives a false sense of safety. EAFP, by contrast, wraps the open itself in a try-except, handling the failure exactly when it occurs and eliminating the gap.

Why this answer

In a multi-threaded web server, the LBYL approach (checking with os.path.exists) introduces a classic TOCTOU (Time of Check, Time of Use) race condition: between the existence check and the actual file open, another thread could delete or rename the file, causing the open to fail despite the check passing. Python's EAFP idiom (try-except) avoids this window by attempting the operation directly and handling the exception if it fails, which is inherently atomic with respect to the file system state. This is why official Python documentation recommends EAFP over LBYL in concurrent environments.

Exam trap

Python Institute often tests the misconception that try-except is purely about style or performance, when in reality the critical exam point is that LBYL introduces race conditions in concurrent code, making EAFP the safer and recommended pattern.

How to eliminate wrong answers

Option A is wrong because readability is subjective and not the strongest technical counterargument; if statements can be written clearly, and the core issue is correctness, not style. Option B is wrong because while try-except can catch multiple exception types cleanly, this is a convenience feature and does not address the fundamental race condition problem in concurrent file access. Option C is wrong because try-except blocks do have a small performance cost when an exception is raised (though negligible in I/O-bound code), but the claim that they have 'no performance cost at all' is factually incorrect and misses the point that the primary concern is correctness, not micro-optimization.

48
MCQhard

What is the result of 'abcdef'[::-2]?

A.'dfb'
B.'ace'
C.'fdb'
D.'eca'
AnswerC

'fdb' is the correct result of the slice 'abcdef'[::-2]. The negative step tells Python to traverse the sequence backward from the last character, selecting 'f' (index 5), then 'd' (index 3), then 'b' (index 1). This is the only option that matches the requested backward, every-other-character behavior.

Why this answer

The slicing syntax [::-2] means start from the end (default step negative), go to the beginning, and take every second character in reverse order. For 'abcdef', starting at 'f' (index -1), then skipping one to 'd' (index -3), then 'b' (index -5), resulting in 'fdb'. Option C is correct.

Exam trap

Candidates often mistakenly think that [::-2] starts from the beginning and skips every two characters forward, leading them to pick 'ace' (option B) instead of understanding that a negative step reverses the traversal order.

How to eliminate wrong answers

Option A is wrong because 'dfb' would require a step of -2 starting from index -2 ('e'), which is not what [::-2] does. Option B is wrong because 'ace' is the result of a positive step of 2 from the beginning (i.e., 'abcdef'[::2]), not a negative step. Option D is wrong because 'eca' would be the result of reversing the string and then taking every second character from the start (i.e., 'fedcba'[::2]), which is a different operation.

49
MCQeasy

A Python script uses a third-party library 'requests'. The developer wants to ensure that the exact version 2.25.1 is installed in the project's environment. Which tool and command should be used?

A.pip install requests
B.pip install requests==2.25.1
C.pip3 install requests==2.25.1
D.pip instll requests==2.25.1
AnswerB, C

This command correctly uses the pip package manager with an explicit version specifier. The == operator, an exact version pin defined by PEP 440, tells pip to install precisely requests 2.25.1 from PyPI, bypassing any newer or older release. This ensures reproducible dependency behavior across different machines and deployment stages.

Why this answer

Both options B and C are correct because they use the standard pip syntax for pinning a specific version: `package==version`. The command `pip install requests==2.25.1` (B) works on systems where `pip` is linked to Python 3, while `pip3 install requests==2.25.1` (C) explicitly invokes the Python 3 version of pip. Both achieve the same result—installing requests exactly version 2.25.1.

Option A fails to specify a version, and option D contains a typo ('instll') that will fail.

Exam trap

A common pitfall is assuming that `pip3` is incorrect or non-standard. In reality, both `pip` and `pip3` are valid commands for Python 3 environments; the key is the version-pinning syntax (`==`). The question tests whether the candidate recognizes the correct syntax for specifying an exact version, not the distinction between `pip` and `pip3`.

How to eliminate wrong answers

Option A is wrong because `pip install requests` installs the latest available version of the library, not the exact version 2.25.1, which fails the requirement for version pinning. Option C is wrong because `pip3` is simply an alias for `pip` on many systems (or a Python 3-specific variant) and does not change the version specification; the command is functionally identical to option B, but the question asks for the correct tool and command, and `pip` is the standard tool name. Option D is wrong because `pip instll` contains a typo ('instll' instead of 'install'), which would cause the command to fail with a 'command not found' error.

50
MCQmedium

During development, a programmer modifies a module that is already imported in the current Python session. To see the changes without restarting the interpreter, which function from the importlib module should be called?

A.reload()
B.reload_module()
C.importlib.reload()
D.importlib.import_module()
AnswerC

This is the correct way to reload a module in Python 3. It takes a module object (already imported) and re-executes its source code, updating the module's attributes in place. It is particularly useful during development to pick up changes without restarting the interpreter. Note that it returns the updated module object, and other references to the old module still point to the same object (since it mutates in place).

Why this answer

`importlib.reload()` is the official Python function to re-import a previously imported module, applying any changes made to its source code without restarting the interpreter. It is part of the `importlib` module and is the recommended way to reload modules in Python 3.

Exam trap

Python Institute often tests the distinction between the Python 2 built-in `reload()` and the Python 3 `importlib.reload()` syntax, and candidates mistakenly choose the bare `reload()` option without realizing it is no longer a built-in function.

How to eliminate wrong answers

Option A is wrong because `reload()` is not a standalone built-in function; in Python 2 it existed as a built-in, but in Python 3 it was moved to `importlib` and must be called as `importlib.reload()`. Option B is wrong because `reload_module()` is not a valid function in the `importlib` module; the correct function name is `reload()`. Option D is wrong because `importlib.import_module()` is used to import a module programmatically, not to reload an already imported module; it does not update the existing module object in memory.

51
MCQhard

Which of the following correctly uses `__slots__` to restrict attribute creation to only `x` and `y`?

A.`class Foo: __slots__ = 'x'`
B.`class Foo: __slots__ = ('x')`
C.`class Foo: __slots__ = ['x', 'y']`
D.`class Foo: __slots__ = ('x', 'y')`
AnswerC, D

A list such as ['x', 'y'] is also a valid iterable, so Python will happily consume it and create exactly those two slots. The interpreter does not require an immutable type for __slots__; it simply iterates over the object to collect the attribute names. However, because the list remains mutable and is stored as a class attribute, a later append or rebind could change the set of allowed attributes, which is why tuples are the conventional, safer choice.

Why this answer

Options C and D are both correct because `__slots__` must be assigned an iterable of strings. A list `['x', 'y']` and a tuple `('x', 'y')` are both valid iterables that restrict attribute creation to exactly `x` and `y`. Option A uses a single string, which would restrict to individual characters `'x'`; Option B uses a string in parentheses without a trailing comma, which is also just a string.

Both A and B would produce unexpected behavior.

Exam trap

Python Institute often tests the misconception that a single string or a parenthesized string without a trailing comma is a valid iterable for `__slots__`, leading candidates to pick options that inadvertently restrict attributes to individual characters rather than the intended attribute names. Additionally, candidates may overlook that a list is also a valid iterable.

How to eliminate wrong answers

Option A is wrong because `__slots__ = 'x'` assigns a single string, which is iterable (yielding characters 'x'), but this restricts attributes to the single character 'x', not the intended attribute name `x`. Option B is wrong because `__slots__ = ('x')` is not a tuple — parentheses without a trailing comma create just the string `'x'`, which again iterates over characters. Option C is wrong because while `['x', 'y']` is a valid iterable and would work technically, the question asks for the correct use to restrict to `x` and `y`; however, the exam considers tuples as the canonical form for `__slots__`, and using a list is less common but not incorrect — but the question's correct answer is D as the most standard and unambiguous form.

52
MCQeasy

A developer wants to convert a string 'Python' to all uppercase letters. Which string method should be used?

A.capitalize()
B.title()
C.swapcase()
D.upper()
AnswerD

The `str.upper()` method returns a new string with all alphabetic characters converted to uppercase, leaving non-alphabetic characters unchanged. For the string `'Python'`, it produces `'PYTHON'` without modifying the original string, satisfying the requirement for a non-destructive transformation. This method operates on each Unicode character’s case mapping, ensuring correct conversion for the given ASCII input.

Why this answer

The `upper()` method returns a copy of the string with all lowercase characters converted to uppercase. Since the goal is to convert 'Python' to 'PYTHON', `upper()` is the correct and most direct method for this task.

Exam trap

The Python PCAP exam often tests the distinction between `upper()` and `capitalize()` or `title()`, where candidates mistakenly choose `capitalize()` thinking it converts the entire string to uppercase, but it only capitalizes the first character.

How to eliminate wrong answers

Option A is wrong because `capitalize()` converts only the first character to uppercase and the rest to lowercase, resulting in 'Python' (no change) or 'python' if the string were all lowercase. Option B is wrong because `title()` capitalizes the first character of each word, which for a single word like 'Python' would produce 'Python' (no change) and is not designed for full uppercase conversion. Option C is wrong because `swapcase()` inverts the case of each character, turning 'Python' into 'pYTHON', not the desired all-uppercase result.

53
MCQeasy

A team is using a shared Python environment where multiple projects have conflicting dependencies. Which approach is the best practice to isolate project dependencies?

A.Create a virtual environment using 'python -m venv' and install dependencies inside it.
B.Manually modify sys.path in each script to include different package directories.
C.Install all dependencies in the system-wide site-packages directory.
D.Install all packages using 'pip install --user' to avoid system conflicts.
AnswerA

Using 'python -m venv' creates an isolated environment with its own Python binary and site-packages directory, allowing each project to install exactly the dependencies and versions it needs without interfering with other projects. This is the standard, built-in best practice for managing project dependencies in a shared Python environment, and it also makes it easy to generate a reproducible requirements.txt for teammates.

Why this answer

Using `python -m venv` creates an isolated virtual environment with its own `site-packages` directory, preventing dependency conflicts between projects. This is the standard best practice recommended by the Python Packaging Authority (PyPA) for managing project-specific dependencies without affecting the system-wide Python installation.

Exam trap

The trap here is that candidates may think `pip install --user` provides isolation similar to a virtual environment, but it only separates user-level from system-level packages, not between projects, so it fails to solve the core problem of conflicting dependencies across multiple projects.

How to eliminate wrong answers

Option B is wrong because manually modifying `sys.path` in each script is fragile, error-prone, and does not isolate dependencies at the package level—it only alters the module search path, leaving the global environment unchanged and still susceptible to version conflicts. Option C is wrong because installing all dependencies in the system-wide `site-packages` directory directly causes the very conflicts the team is trying to avoid, as different projects may require different versions of the same package. Option D is wrong because `pip install --user` installs packages in the user-specific `site-packages` directory (e.g., `~/.local/lib/pythonX.Y/site-packages`), which is shared across all projects run by that user, so it does not provide per-project isolation and can still lead to dependency conflicts.

54
MCQhard

A cloud infrastructure engineer is developing a Python script to parse large configuration files from a fleet of servers. Each file can be up to 500 MB. The script reads the file line by line using a file object, strips comment lines (those starting with '#'), and accumulates only the configuration directives into a single string for further processing. The current code is: ```python result = '' with open('config.cfg') as f: for line in f: if not line.startswith('#'): result += line.strip() ``` After processing just a few hundred lines of a large file, the script becomes extremely slow and consumes an excessive amount of memory. The engineer identifies that string concatenation using `+=` is inefficient because strings are immutable, causing repeated memory reallocation. Which approach should the engineer implement to resolve the performance issue without changing the final output?

A.Replace `result += line.strip()` with `result = result + line.strip()`.
B.Use `io.StringIO` to write lines and then retrieve content with `.getvalue()`.
C.Use `str.join` called on the file object: `f.join('')`.
D.Use a list to collect stripped lines and then call `''.join(lines)` after the loop.
AnswerD

Store each stripped line as an element in a list during the loop; appending to a list is amortized O(1). After the loop, call `''.join(lines)` to allocate the final string exactly once and copy each part in a single pass, producing O(n) total work. This is the canonical idiom because it avoids repeated string reallocation and takes advantage of `str.join`'s optimized internal traversal of the sequence.

Why this answer

It avoids the O(n²) time complexity of repeated string concatenation by collecting stripped lines in a list and then joining them once with `''.join(lines)`. This leverages the efficient memory allocation of `str.join`, which precomputes the total size and allocates exactly once, solving the performance and memory issue without altering the final output.

Exam trap

Candidates often incorrectly believe that `result = result + line.strip()` is more efficient than `result += line.strip()`, but both have the same O(n^2) performance due to string immutability. The correct solution is to collect lines in a list and join them with `''.join()`.

How to eliminate wrong answers

Option A is wrong because `result = result + line.strip()` is semantically identical to `result += line.strip()` — both create a new string object and cause the same O(n²) reallocation overhead. Option B is wrong because `io.StringIO` is designed for in-memory text streams and would still require a final `.getvalue()` call, but it does not inherently solve the concatenation inefficiency; it adds unnecessary overhead for this simple accumulation task. Option C is wrong because `str.join` is a method on a string separator, not on a file object; `f.join('')` would raise an `AttributeError` since file objects have no `join` method.

55
MCQeasy

A developer writes a script to read a configuration file that may not exist. The script should handle the error gracefully and continue. Which approach is most Pythonic?

A.Use a try-except block catching FileNotFoundError
B.Use a try-except block catching OSError
C.Use os.path.exists to check, then open if it exists
D.Use an if statement to check file size
AnswerA

EAFP; clean and recommended for this scenario.

Why this answer

It directly catches the specific `FileNotFoundError` exception, which is a subclass of `OSError` and is raised when a file does not exist. This approach follows the Pythonic principle of EAFP (Easier to Ask for Forgiveness than Permission), allowing the script to attempt the operation and handle the failure gracefully without redundant checks.

Exam trap

Python Institute often tests the distinction between catching a specific exception (`FileNotFoundError`) versus a broader parent exception (`OSError`), and the trap here is that candidates may choose the broader catch thinking it is safer, without realizing it can mask other critical errors.

How to eliminate wrong answers

Option B is wrong because catching `OSError` is too broad; it would also catch other operating system errors (e.g., permission denied, disk full) that may require different handling, masking the specific file-not-found scenario. Option C is wrong because using `os.path.exists` introduces a race condition (TOCTOU — Time of Check to Time of Use) where the file could be deleted or created between the check and the open call, and it violates the Pythonic EAFP idiom by using LBYL (Look Before You Leap). Option D is wrong because checking file size does not determine if a file exists; a file with zero size exists, and a non-existent file has no size to check, making this approach logically incorrect and unreliable.

56
MCQmedium

You are developing a package 'analytics' that contains subpackages 'stats' and 'ml'. The __init__.py of 'analytics' imports a function 'normalize' from 'analytics.stats'. When a user runs `import analytics`, they get an ImportError. Which change ensures the package imports correctly?

A.Change the import to: from stats import normalize
B.Add sys.path.append('.') before the import in __init__.py
C.Change the import in analytics/__init__.py to: from .stats import normalize
D.Move the import statement to the stats/__init__.py file
AnswerC

`from .stats import normalize` is a relative import: the leading dot tells CPython's import machinery to start from the current package, `analytics`, and resolve `.stats` as `analytics.stats` (PEP 328). This works regardless of the absolute `sys.path` layout, whether `analytics` is installed as a package or invoked from a script, and it avoids name collisions with any unrelated top-level `stats` module. Since the statement appears in `analytics/__init__.py`, `.stats` is unambiguous and correctly loads the sibling submodule, making `normalize` available as `analytics.normalize`.

Why this answer

It uses an explicit relative import (`from .stats import normalize`), which is the proper way to import from a subpackage within a package. Absolute imports like `from analytics.stats import normalize` can fail if the package's parent directory is not in `sys.path`, which is common when running scripts directly. Relative imports resolve correctly based on the package structure, ensuring the import works regardless of how the package is invoked.

Exam trap

Python Institute often tests the distinction between absolute and relative imports in packages, and the trap here is that candidates mistakenly think absolute imports like `from analytics.stats import normalize` are always safe, not realizing they depend on the package being installed or the parent directory being in `sys.path`.

How to eliminate wrong answers

Option A is wrong because `from stats import normalize` uses an absolute import without the package prefix, which will look for a top-level module named `stats` rather than the subpackage `analytics.stats`, causing a ModuleNotFoundError. Option B is wrong because `sys.path.append('.')` adds the current working directory to the module search path, which is unreliable and does not guarantee that the package's parent directory is in `sys.path`; it also violates best practices by modifying `sys.path` in `__init__.py`. Option D is wrong because moving the import to `stats/__init__.py` would not make `normalize` available at the `analytics` package level when a user runs `import analytics`; the import must be in `analytics/__init__.py` to be part of the package's namespace.

57
MCQmedium

A team is developing a data processing pipeline where each step is a class that implements a common interface. They have defined an abstract base class DataProcessor with an abstract method process(data). Several concrete subclasses implement process. Now they need to add a new step that logs the data before processing. They want to reuse the existing processing logic without modifying the original classes. Which design pattern should they apply?

A.Factory pattern to instantiate processors dynamically.
B.Decorator pattern by creating a LoggingProcessor subclass that wraps another processor and calls its process method after logging.
C.Singleton pattern to ensure only one logger exists.
D.Observer pattern to notify loggers of data changes.
AnswerB

The Decorator pattern is correct because it lets you attach new responsibilities to an object without modifying its class. A LoggingProcessor subclass that holds a reference to another processor and invokes its process method after emitting log output is the canonical decorator implementation, preserving the processor interface while transparently adding logging. This wrapper can be applied to any processor instance at runtime and can even be stacked with other decorators.

Why this answer

The Decorator pattern allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. By creating a LoggingProcessor that wraps an existing DataProcessor and delegates to its process method after logging, the team reuses the original processing logic without modifying the existing classes, adhering to the Open/Closed Principle.

Exam trap

Python Institute often tests the Decorator pattern in scenarios where the requirement is to add responsibilities to objects dynamically without altering their structure, and the trap is that candidates confuse it with the Factory pattern because both involve creating objects, but the Decorator focuses on extending behavior, not on instantiation logic.

How to eliminate wrong answers

Option A is wrong because the Factory pattern is used to encapsulate object creation logic, not to add new behavior to existing objects; it would not help in adding logging without modifying the original classes. Option C is wrong because the Singleton pattern ensures a single instance of a class (e.g., a logger), but it does not provide a mechanism to wrap or extend the behavior of existing DataProcessor objects. Option D is wrong because the Observer pattern defines a one-to-many dependency for event notification, which is not suitable for wrapping a single processor to add logging before its execution.

58
MCQeasy

A developer defines a class with an __init__ method that sets instance attributes. Which of the following is the correct way to call the parent class's __init__ from a child class?

A.super(self, Child).__init__(arg1, arg2)
B.Parent.__init__(self, arg1, arg2)
C.Child.__init__(self, arg1, arg2)
D.super().__init__(arg1, arg2)
AnswerD

super().__init__(arg1, arg2) is the idiomatic Python 3 way to invoke the parent initializer. The zero-argument super() call automatically captures the current class and instance via the compiler's __class__ cell, returning a proxy that resolves to the next class in the MRO. This preserves cooperative multiple inheritance, ensuring that each class in the hierarchy is initialized correctly and that diamond dependencies are handled safely.

Why this answer

`super().__init__(arg1, arg2)` is the modern, recommended way to call the parent class's `__init__` method in Python. It uses the `super()` function without arguments to automatically resolve the parent class based on the method resolution order (MRO), ensuring proper cooperative multiple inheritance and avoiding hardcoding the parent class name.

Exam trap

The PCAP exam often tests the misconception that `super()` requires explicit arguments or that calling the parent class directly by name (e.g., `Parent.__init__(self, ...)`) is the standard or recommended approach, when in fact `super().__init__(...)` is the Pythonic way and is required for proper MRO handling in complex hierarchies.

How to eliminate wrong answers

Option A is wrong because `super(self, Child).__init__(arg1, arg2)` incorrectly passes `self` as the first argument and `Child` as the second; the correct syntax is `super(Child, self).__init__(arg1, arg2)` or, more simply, `super().__init__(arg1, arg2)`. Option B is wrong because `Parent.__init__(self, arg1, arg2)` is an explicit call that bypasses the MRO and can break cooperative multiple inheritance, though it works in single inheritance; it is not the 'correct' way in modern Python. Option C is wrong because `Child.__init__(self, arg1, arg2)` would call the child class's own `__init__` method, leading to infinite recursion and a `RecursionError`.

59
MCQhard

Consider the code fragment: f = open('data.txt', 'r') data = f.read() process_data(data) f.close() What is the primary risk if an exception occurs during process_data(data)?

A.The file descriptor may be leaked because the close() call is skipped.
B.The exception will be silently suppressed.
C.The file will be automatically closed by Python's garbage collector immediately.
D.The file contents will be corrupted.
AnswerA

Correct: if an exception occurs, close() is not called.

Why this answer

If `process_data(data)` raises an exception, the `f.close()` statement is never executed, leaving the file descriptor open. This is a resource leak that can exhaust system file handles, especially in long-running applications. Python's `with` statement is the recommended approach to guarantee automatic cleanup even when exceptions occur.

Exam trap

Python Institute often tests the misconception that Python's garbage collector immediately closes files, when in reality it only closes them during an unpredictable collection cycle, making explicit cleanup essential.

How to eliminate wrong answers

Option B is wrong because exceptions are not silently suppressed; they propagate up the call stack unless caught by an explicit `try-except` block. Option C is wrong because Python's garbage collector does not immediately close file descriptors; it may close them at an indeterminate time, and relying on it is poor practice and can lead to resource exhaustion. Option D is wrong because an exception during `process_data(data)` does not corrupt the file contents on disk; the file was opened in read mode, and the data is already read into memory before the exception occurs.

60
Multi-Selectmedium

Which three statements about the Method Resolution Order (MRO) in Python are true? (Choose three.)

Select 3 answers
A.The MRO can be viewed using the __mro__ attribute.
B.MRO is determined by the C3 linearization algorithm.
C.The MRO is only used for methods, not attributes.
D.In diamond inheritance, the topmost base class is visited last.
E.The MRO can be changed by modifying the class hierarchy at runtime.
AnswersA, B, D

Each class has a __mro__ attribute showing the order.

Why this answer

Every Python class has an `__mro__` attribute that returns a tuple of classes in the order they are searched for methods and attributes. This attribute is automatically generated by the C3 linearization algorithm and provides a clear, inspectable view of the resolution order.

Exam trap

Python Institute often tests the misconception that the MRO only applies to methods, when in fact it governs all attribute lookups, including data attributes and descriptors.

61
MCQmedium

Refer to the exhibit. What is the output when this code is executed?

A.AttributeError
B.0
C.None
D.100
AnswerA

Name mangling prevents direct access to __balance.

Why this answer

The code attempts to access the attribute `balance` on an instance of `BankAccount`, but the class uses `__balance` (double underscore) in the `__init__` method, which triggers Python's name mangling. The attribute is actually stored as `_BankAccount__balance`. Therefore, `balance` does not exist on the instance, and accessing it raises an `AttributeError`.

In Python, accessing a non-existent attribute raises an error rather than returning a default value.

Exam trap

Python Institute often tests the misconception that accessing a missing attribute in Python returns a default value like `None` or `0`, when in fact it raises an `AttributeError` unless the class defines `__getattr__` or `__getattribute__`.

How to eliminate wrong answers

Option B is wrong because it suggests the output is `0`, which would only happen if `balance` were explicitly initialized to `0` in `__init__` (e.g., `self.balance = 0`), but no such assignment exists. Option C is wrong because `None` would be returned only if `balance` were a method that returns nothing, or if the attribute existed and was set to `None`; here the attribute does not exist at all. Option D is wrong because `100` would require `balance` to be set to `100` somewhere, such as via `self.balance = 100` in `__init__` or after a deposit, but the code never creates or assigns a `balance` attribute.

62
MCQhard

A developer writes a class 'Logger' with a class method 'log(msg)' that writes to a file. Another class 'AppLogger' inherits from 'Logger'. The developer expects both classes to share the same file handle. However, after creating an instance of 'AppLogger', the file handle is different. What is the most likely cause?

A.The 'log' method is defined as a class method using @classmethod
B.The file handle is opened in the __init__ method of the base class
C.The file handle is stored as a private attribute __file
D.The subclass overrides the 'log' method
AnswerB

Opening the file handle inside __init__ assigns the result to an instance attribute (via self), so each time a new Logger or subclass object is created, a separate descriptor is opened and stored on that specific instance. Because the handle is not attached to the class object, no sharing occurs between instances. This directly contradicts the premise that a single logger's file handle is shared, making this the correct flaw in the developer's code.

Why this answer

If the file handle is opened in the `__init__` method of the base class, each time a new instance is created (including when an `AppLogger` instance is created), a new file handle is opened. This means the `Logger` class and the `AppLogger` class do not share the same file handle; instead, each instance gets its own handle. To share a single file handle across all instances, the file handle should be opened as a class attribute or in a class method, not in `__init__`.

Exam trap

The trap here is that candidates often confuse instance attributes with class attributes, assuming that inheritance automatically shares instance-level resources, when in fact each instance gets its own copy of attributes defined in `__init__`.

How to eliminate wrong answers

Option A is wrong because using `@classmethod` for the `log` method does not cause different file handles; it simply means the method receives the class as the first argument, not the instance. The file handle sharing issue is about where the handle is opened, not the method decorator. Option C is wrong because storing the file handle as a private attribute `__file` (name mangling) does not inherently cause different handles; it only affects attribute access from subclasses.

The core issue remains that the handle is opened per instance in `__init__`. Option D is wrong because overriding the `log` method in the subclass would change the behavior of logging, but it would not cause the file handle to be different unless the override itself opens a new handle. The question states the developer expects both classes to share the same handle, and the problem is that after creating an instance of `AppLogger`, the handle is different—this points to the handle being created per instance, not to an override.

63
MCQeasy

In Python, if you have a try block followed by an except clause that catches all exceptions, which of the following is true about the else clause?

A.The else clause runs only if no exception is raised in the try block.
B.The else clause runs only if an exception occurs.
C.The else clause runs before the finally block regardless of exceptions.
D.The else clause is used to specify additional exception handlers.
AnswerA

The else clause is part of a try/except/else/finally compound statement. It executes only when the try block completes without raising any exception, meaning control flows to else immediately after the last statement in try. If an exception occurs, else is skipped and control jumps to a matching except handler instead. This makes else ideal for code that should run only on success, such as logging successful completion or proceeding with a computed result.

Why this answer

In Python, the `else` clause in a `try` statement executes only if no exception was raised in the `try` block. This is true regardless of whether the `except` clause catches all exceptions (e.g., bare `except:` or `except Exception:`). The `else` block is specifically designed for code that should run only when the `try` block completes successfully without any exception.

Exam trap

The trap here is that candidates often confuse the `else` clause with a second chance to handle exceptions or think it runs unconditionally before `finally`, when in fact it is strictly tied to the successful execution of the `try` block and is skipped entirely if any exception occurs.

How to eliminate wrong answers

Option B is wrong because the `else` clause runs only when no exception occurs, not when an exception occurs; code that runs on an exception belongs in the `except` block. Option C is wrong because the `else` clause runs before the `finally` block only if no exception was raised, but if an exception is raised, the `else` block is skipped entirely and the `finally` block still runs; the order is not guaranteed to be `else` before `finally` in all cases. Option D is wrong because the `else` clause is not used to specify additional exception handlers; additional exception handlers are specified by additional `except` clauses, while the `else` clause is for code that executes only on successful completion of the `try` block.

64
MCQeasy

A developer creates a package named 'mypkg' with an __init__.py file. Inside the package, there is a module 'utils.py'. Which of the following is the correct way to import the function 'helper' from 'utils' from outside the package?

A.import mypkg.utils.helper
B.import mypkg; mypkg.utils.helper
C.from mypkg.utils import helper
D.from mypkg import utils.helper
AnswerC

This is the correct and idiomatic form because it explicitly names the submodule `utils` and the object `helper` in the `from ... import ...` syntax. The statement `from mypkg.utils import helper` tells Python to load `mypkg/utils` (the submodule) and then extract the attribute `helper` from that module's namespace, binding it locally as `helper`. This is the standard approach for importing a function or variable from a submodule directly, avoiding the need for dotted attribute chains.

Why this answer

It uses the standard Python syntax for importing a specific name from a submodule within a package: `from package.module import name`. This directly imports the `helper` function into the current namespace, making it callable without any prefix. The `__init__.py` file marks `mypkg` as a package, and `utils.py` is a module inside it, so `from mypkg.utils import helper` is the proper way to access `helper` from outside the package.

Exam trap

Python Institute often tests the distinction between importing a module versus importing an attribute from a module, and the trap here is that candidates confuse the `import` statement (which only accepts modules/packages) with the `from ... import` statement (which can import any object), leading them to choose Option A or D.

How to eliminate wrong answers

Option A is wrong because `import mypkg.utils.helper` attempts to import a module named `helper`, but `helper` is a function, not a module; Python's import system only supports importing modules or packages, not individual objects like functions or classes, via the `import` statement. Option B is wrong because `mypkg.utils.helper` is not a valid attribute access after `import mypkg`; `import mypkg` only imports the top-level package, and to access `utils` you would need to import `mypkg.utils` explicitly (e.g., `import mypkg.utils`), otherwise `mypkg.utils` is undefined. Option D is wrong because `from mypkg import utils.helper` uses dot notation in the import name, which is invalid syntax; the `from ... import` statement expects a single module or a comma-separated list of names, not a dotted path to an attribute.

65
MCQmedium

A programmer uses a class method to create an alternative constructor for a `Point` class. The method should parse a string like "10,20" and return a `Point` instance with x=10, y=20. Which code snippet correctly implements this?

A.`def from_string(self, s):\n parts = s.split(',')\n return Point(int(parts[0]), int(parts[1]))`
B.`@staticmethod\ndef from_string(s):\n parts = s.split(',')\n return Point(int(parts[0]), int(parts[1]))`
C.`def from_string(cls, s):\n parts = s.split(',')\n return cls(int(parts[0]), int(parts[1]))`
D.`@classmethod\ndef from_string(cls, s):\n parts = s.split(',')\n return cls(int(parts[0]), int(parts[1]))`
AnswerD

This is the canonical alternative constructor pattern: the @classmethod decorator makes Python bind the actual class object to the cls parameter, so calling Point.from_string(...) passes Point as cls. Using cls(parts[0], parts[1]) instead of Point(...) means the method respects inheritance — a subclass that inherits from_string will construct instances of that subclass, not the base class. This is exactly how standard library methods such as datetime.fromtimestamp and dict.fromkeys work.

Why this answer

It uses the `@classmethod` decorator, which automatically passes the class (`cls`) as the first argument. This allows the method to create an instance of the class using `cls(...)`, making it a proper alternative constructor that works correctly even if the class is subclassed. The method parses the string "10,20" by splitting on the comma and converting the parts to integers.

Exam trap

The PCAP exam often tests the distinction between `@classmethod` and `@staticmethod` by presenting a method that looks like it should be a static method but actually needs access to the class for proper inheritance, tempting candidates to choose the static version or a plain method without a decorator.

How to eliminate wrong answers

Option A is wrong because it defines a regular instance method with `self` as the first parameter, but it is called on the class (not an instance), so `self` would receive the string argument, causing a TypeError or incorrect behavior. Option B is wrong because it uses `@staticmethod`, which does not receive the class as an argument; it hardcodes `Point` instead of using `cls`, so it does not support inheritance properly and is not a true alternative constructor. Option C is wrong because it lacks a decorator, so Python treats it as a regular instance method; the first parameter `cls` would be interpreted as `self`, leading to a mismatch when called on the class.

66
Matchingmedium

Match each exception to its cause.

Drag a concept onto its matching description — or click a concept then click the description.

Concepts
Matches

Operation on incompatible type

Function receives argument with correct type but invalid value

Sequence subscript out of range

Mapping key not found

Attribute reference or assignment fails

Why these pairings

Correct matches: ValueError with inappropriate value, TypeError with wrong type, IndexError with out-of-range index, KeyError with missing key. Common confusions arise from swapping the definitions of ValueError and TypeError, or TypeError and KeyError.

67
MCQmedium

A developer needs to parse a log file where each line contains a timestamp followed by a message. The timestamp format is 'YYYY-MM-DD HH:MM:SS'. Which string method is most appropriate to split the timestamp from the message?

A.str.rsplit()
B.str.splitlines()
C.str.partition()
D.str.split()
AnswerD

str.split() with no arguments splits on any run of whitespace, trimming leading and trailing spaces, and returns a list of non-empty substrings. For a log line like '2025-04-10 14:22:31 INFO message here', the timestamp (which contains no spaces) becomes the first element while the rest of the line is broken into subsequent elements, cleanly isolating the timestamp. It is the most direct method because it handles variable amounts of whitespace without requiring a separator to be specified.

Why this answer

Str.split(), is the most appropriate because it splits a string on whitespace by default. Although the timestamp 'YYYY-MM-DD HH:MM:SS' contains a space, using split() without arguments returns a list of all space-separated elements. Since the timestamp is always the first two elements (date and time), the developer can join them with a space to get the full timestamp.

Alternatively, split() can be used with a specified separator and maxsplit to achieve the desired split. This flexibility makes str.split() the best choice among the given options.

Exam trap

Python Institute often tests the distinction between str.split() and str.partition(), where candidates mistakenly choose str.partition() because they think it splits on the first space, but fail to realize that the timestamp itself contains a space, causing an incorrect split.

How to eliminate wrong answers

Option A is wrong because str.rsplit() splits from the right side of the string, which would incorrectly separate the last word of the message rather than the first space after the timestamp. Option B is wrong because str.splitlines() splits on line boundaries (newline characters), not on whitespace within a single line, so it cannot separate the timestamp from the message on the same line. Option C is wrong because str.partition() splits on the first occurrence of a specific separator string, but the timestamp contains spaces (between date and time), so using a space as the separator would split the timestamp itself, not separate it from the message.

68
MCQeasy

A developer creates a Python class with a method that is intended to be overridden in subclasses. Which approach best ensures that the method is not accidentally called on the base class?

A.Use 'pass' as the method body
B.Delete the method from the base class using 'del'
C.Add a comment '# override in subclass' inside the method body
D.Raise NotImplementedError inside the method body
AnswerD

Raising NotImplementedError clearly signals the method must be overridden.

Why this answer

Raising NotImplementedError inside the base class method is the standard Python idiom for defining an abstract-like method that must be overridden in subclasses. If a subclass fails to override the method and it is called, Python will raise an explicit error at runtime, preventing accidental use of the base implementation. This approach enforces the contract that the method is intended only for subclasses, without requiring the `abc` module.

Exam trap

Python Institute often tests the distinction between documentation-based approaches (comments) and runtime enforcement (exceptions), leading candidates to mistakenly choose a comment or 'pass' as sufficient for preventing accidental base class usage.

How to eliminate wrong answers

Option A is wrong because using 'pass' as the method body creates a no-op method that silently does nothing when called on the base class, which defeats the purpose of preventing accidental invocation. Option B is wrong because deleting the method from the base class with 'del' would cause an AttributeError when the method is called on a base class instance, but it also prevents subclasses from inheriting and overriding the method, breaking the intended design. Option C is wrong because adding a comment '# override in subclass' inside the method body has no runtime effect; it is merely a documentation hint that does not enforce or prevent any behavior.

69
MCQhard

Refer to the exhibit. Which statement about the output is true?

A.a.__class__.__bases__ returns an empty tuple
B.type(a).__bases__ returns (<class 'object'>,)
C.a.__class__.__bases__ returns (<class 'object'>,)
D.Both print statements output the same thing
AnswerC

Correct: a.__class__ is class A, and A.__bases__ is (object,).

Why this answer

`a.__class__` returns the class of instance `a`, which is `A`. Since `A` does not explicitly inherit from any class, it implicitly inherits from `object`. Therefore, `A.__bases__` returns `(<class 'object'>,)`, which is exactly what `a.__class__.__bases__` evaluates to.

Exam trap

The trap here is that candidates often confuse `a.__class__` with `type(a)` and assume they always return the same thing, but the PCAP exam tests the subtle difference that `__class__` is an attribute of the instance while `type()` is a built-in function, and for instances of classes that override `__class__`, they can differ, leading to different `__bases__` results.

How to eliminate wrong answers

Option A is wrong because `a.__class__.__bases__` does not return an empty tuple; it returns `(<class 'object'>,)` since every class in Python 3 implicitly inherits from `object`. Option B is wrong because `type(a).__bases__` is equivalent to `A.__bases__`, which returns `(<class 'object'>,)`, not `(<class 'object'>,)` — wait, that is actually the same tuple; the error is that the option incorrectly states the return value as `(<class 'object'>,)` when it should be `(<class 'object'>,)` — but the real mistake is that `type(a).__bases__` does not return `(<class 'object'>,)`? Actually, it does; the trap is that `type(a)` returns `<class 'A'>`, and `A.__bases__` is indeed `(<class 'object'>,)`, so Option B is factually correct in value but the question expects the attribute access via `a.__class__` to be the correct form, making B a distractor because it uses `type(a)` instead of `a.__class__`. Option D is wrong because the two print statements output the same thing only if `a.__class__` and `type(a)` return the same class object, which they do for a normal instance, but the question's exhibit likely shows that `a.__class__` and `type(a)` are identical, so the outputs are the same — however, the statement 'Both print statements output the same thing' is false in the context of the exhibit because the exhibit shows different outputs? Actually, the exhibit is not provided, but the correct answer is C, implying that the two print statements do NOT output the same thing, likely because one accesses `__bases__` on the class and the other on the type, but they are the same; the trap is that the exhibit might show a subtle difference, so D is wrong because the outputs are not identical.

70
MCQhard

A developer wants a class 'LoggedDict' that behaves like a dict but logs all attribute access in the console. Which method override correctly implements this for getting an attribute?

A.def __get__(self, instance, owner): print(f'Access'); return self
B.def __getattribute__(self, name): print(f'Access {name}'); return super().__getattribute__(name)
C.def __getattr__(self, name): print(f'Access {name}'); return self.__dict__[name]
D.def __getitem__(self, key): print(f'Access {key}'); return dict.__getitem__(self, key)
AnswerB

This correctly overrides __getattribute__, which Python invokes for every normal attribute access using dot notation on an instance. It prints the attribute name, then delegates to super().__getattribute__(name) to perform the real lookup—this call to the parent implementation is essential both to return the actual attribute value and to avoid infinite recursion. In a loggeddict subclass, this will log access to keys like obj.name, obj.method, and inherited attributes, though implicit special-method calls may bypass it.

Why this answer

`__getattribute__` is the universal method called for every attribute access on an object. By overriding it, the developer can log the attribute name before delegating to the superclass implementation via `super().__getattribute__(name)`, which preserves the normal attribute lookup chain. This ensures that all attribute accesses (including those that exist and those that don't) are logged, which is the requirement for 'LoggedDict'.

Exam trap

Python Institute often tests the distinction between `__getattribute__` (called for every attribute access) and `__getattr__` (called only as a fallback when the attribute is not found), leading candidates to mistakenly choose `__getattr__` because it seems simpler or because they confuse it with the general 'get attribute' concept.

How to eliminate wrong answers

Option A is wrong because `__get__` is the descriptor protocol method, invoked when an attribute is accessed on a class that owns a descriptor instance, not for general attribute access on a dict-like object. Option C is wrong because `__getattr__` is only called when normal attribute lookup fails (i.e., when `__getattribute__` raises an AttributeError), so it would not log successful accesses; additionally, using `self.__dict__[name]` bypasses the dict's own storage and can cause infinite recursion or missing keys. Option D is wrong because `__getitem__` is used for subscription access (e.g., `obj[key]`), not for attribute access (e.g., `obj.attr`); it would log dictionary key lookups, not attribute accesses.

71
MCQmedium

A developer needs to combine a list of 10,000 strings into a single string. Which approach is most efficient in terms of memory and performance?

A.Use ''.join(string_list)
B.Use a loop with str += to concatenate each string
C.Use str.replace() to merge the strings
D.Use str.format() to build the string step by step
AnswerA

The str.join() method is optimized for this exact use case. It first iterates over string_list to calculate the total length, allocates a single backing buffer of exactly that size, and then copies each string into place without creating any intermediate objects. This results in O(n) time and minimal memory overhead, so it is the canonical and most efficient way to concatenate many strings.

Why this answer

The `''.join(string_list)` method is the most efficient because it pre-allocates memory for the final string by first calculating the total length of all strings in the list, then building the result in a single pass. This avoids the quadratic time complexity and repeated memory reallocations caused by string immutability in Python when using `+=` in a loop.

Exam trap

Python Institute often tests the misconception that `+=` is efficient for string concatenation because it works in other languages, but in Python, string immutability makes it a performance disaster for large lists.

How to eliminate wrong answers

Option B is wrong because using `str +=` in a loop creates a new string object for each concatenation, leading to O(n²) time complexity and excessive memory allocation due to Python's immutable strings. Option C is wrong because `str.replace()` is designed for substring replacement, not concatenation, and would require an initial string to operate on, making it unsuitable and inefficient for merging a list of strings. Option D is wrong because `str.format()` is intended for formatting placeholders, not for concatenating an arbitrary list of strings, and using it iteratively would still involve repeated string creation and poor performance.

72
MCQhard

You are working on a legacy system that processes financial transactions. The system uses a class hierarchy: Transaction (base), Deposit, Withdrawal, Transfer. Each subclass overrides a method 'process()' to handle its specific logic. The code often runs in a multi-threaded environment and you notice intermittent errors where a transaction is processed twice. The logging shows that the same transaction object is being passed to the process method multiple times. The transaction objects are created from a factory function that caches recently used transactions. The errors seem to occur when two threads call the factory at the same time with the same parameters. After investigating, you find that the factory uses a class-level dictionary to cache objects. Which of the following is the most appropriate solution to prevent double processing?

A.Add a lock around the cache lookup and creation in the factory function
B.Add a flag to each transaction object to indicate if it has been processed, and check it at the start of process()
C.Remove the caching mechanism from the factory function to ensure new objects are always created
D.Make the process() method idempotent by checking if the transaction has already been applied to the account (e.g., check balance changes)
AnswerD

Idempotency is the correct design because it makes each transaction carry a natural guard: before applying changes, process() can verify whether the transaction's effects are already reflected in the account (e.g., comparing a journal entry, version number, or resulting balance). In a multi-threaded environment, this check must be atomic with the application step—such as using a database transaction with a unique constraint on the transaction ID—so repeated calls from any thread, queue replay, or retry produce only one net effect. This approach is thread-safe, recoverable after crashes, and eliminates the need to force uniqueness at the object or call-site level.

Why this answer

The core issue is that the same transaction object can be processed multiple times in a multi-threaded environment, even if the factory is fixed. Making process() idempotent by checking whether the transaction has already been applied (e.g., verifying account balance changes) ensures that repeated calls with the same object do not cause duplicate financial effects, directly addressing the symptom of double processing regardless of how the object is cached or retrieved.

Exam trap

Python Institute often tests the misconception that preventing object reuse or adding locks in the factory is sufficient to fix double processing, when the real requirement is to make the operation itself idempotent to handle any scenario where the same object is processed more than once.

How to eliminate wrong answers

Option A is wrong because adding a lock around the cache lookup and creation only prevents race conditions in the factory, but does not prevent the same transaction object from being passed to process() multiple times after it has been created; the double processing can still occur if the object is reused or if the calling code erroneously invokes process() again. Option B is wrong because adding a processed flag to the transaction object is not thread-safe without additional synchronization; two threads could both check the flag before either sets it, leading to a race condition where both proceed to process the transaction, and it also violates the principle of keeping processing logic separate from state management. Option C is wrong because removing the caching mechanism eliminates the performance benefit of reusing objects but does not solve the fundamental problem: the same transaction object could still be passed to process() multiple times from other parts of the code, and without idempotency, double processing would still occur.

73
Multi-Selecteasy

Which TWO of the following statements about class attributes in Python are true?

Select 2 answers
A.Class attributes are always immutable.
B.Class attributes are shared by all instances.
C.Class attributes are defined inside methods.
D.Modifying a class attribute via an instance modifies it for all instances.
E.Class attributes can be accessed via the class name.
AnswersB, E

Because class attributes live in the class's own namespace, the same object is visible to every instance of that class. When you access `inst.x`, Python first checks the instance's `__dict__`, then walks the class MRO, so if no instance-level attribute shadows it, all instances resolve to the identical class attribute. This shared visibility is the defining trait that distinguishes class attributes from instance attributes, which are stored per-object in each instance's `__dict__`.

Why this answer

Class attributes are defined directly in the class body and are shared across all instances of that class. When you access a class attribute via any instance, Python looks up the attribute in the class's __dict__ if it is not shadowed by an instance attribute, ensuring all instances see the same value unless explicitly overridden.

Exam trap

The PCAP exam often tests the subtle distinction between mutating a mutable class attribute (which affects all instances) and reassigning it via an instance (which creates a shadowing instance attribute), leading candidates to incorrectly think that any modification via an instance changes the class attribute for all instances.

74
MCQhard

You are designing a class that should behave like a sequence and support slicing. Which special methods must be implemented?

A.__len__ and __contains__
B.__iter__ and __next__
C.__getitem__ alone
D.__getitem__ and __len__
E.__getitem__ and __setitem__
AnswerD

These two methods are the minimum for a sequence that supports slicing.

Why this answer

For a class to support slicing in Python, it must implement both `__getitem__` (to handle indexing and slice objects) and `__len__` (to define the sequence length, which is required for proper slice boundary handling). Together, these satisfy the sequence protocol, enabling Python's slicing syntax like `obj[start:stop:step]`.

Exam trap

Python Institute often tests the misconception that `__getitem__` alone is enough for slicing, but the trap is that `__len__` is also required for the interpreter to handle slice defaults and negative indices correctly.

How to eliminate wrong answers

Option A is wrong because `__len__` and `__contains__` are not sufficient for slicing; `__contains__` only supports the `in` operator, not indexing or slicing. Option B is wrong because `__iter__` and `__next__` make an object iterable but do not provide indexed access or slicing capabilities. Option C is wrong because `__getitem__` alone can handle basic indexing, but without `__len__`, Python cannot properly compute slice defaults (e.g., `None` for start/stop) or support negative indices in slicing.

Option E is wrong because `__setitem__` is for item assignment, not required for read-only slicing; the sequence protocol for slicing only mandates `__getitem__` and `__len__`.

75
MCQhard

A development team is building a real-time chat application using Python. The application uses a class 'ChatRoom' that maintains a list of 'User' objects as active participants. Each User object holds a reference back to its ChatRoom to send messages. Over time, the application runs out of memory. Profiling reveals that User objects are not being garbage collected even after users disconnect. The team suspects circular references. Which solution would effectively resolve the memory leak without breaking the functionality?

A.Use weakref.WeakSet for the participants list in ChatRoom, so that when a User is no longer referenced elsewhere, it is automatically removed
B.Increase the Python heap size using PYTHON_MALLOC_DEBUG to avoid memory issues
C.Store the ChatRoom reference in User using a weakref.ref, so that the cycle is broken
D.Manually call gc.collect() every time a user disconnects
AnswerA

A WeakSet in ChatRoom holds only weak references to User objects, meaning the set does not participate in reference counting. When the last external strong reference to a User is deleted (e.g., the user logs out and the client connection closes), the object's refcount drops to zero and it is deallocated immediately, automatically removing it from the participants list without any manual cleanup. This breaks the reference cycle between ChatRoom and its participants because the weak reference never increments the reference count, so garbage collection is not needed for removal, and the cycle is resolved as soon as the external strong references vanish.

Why this answer

Using a `weakref.WeakSet` for the participants list in `ChatRoom` means the `ChatRoom` holds only weak references to `User` objects. When a user disconnects and all external references to that `User` are removed, the `User` object becomes unreachable and can be garbage collected, even though the `User` still holds a strong reference back to the `ChatRoom`. This breaks the circular reference without requiring manual intervention or altering the `User`-to-`ChatRoom` relationship.

Exam trap

The key trap here is that weakening the User's reference back to ChatRoom (Option C) does break the circular reference (since one link becomes weak), but it does NOT allow the User to be garbage collected because ChatRoom still holds a strong reference to User. The User remains strongly reachable through ChatRoom, so it stays alive. The correct solution must remove the strong reference from ChatRoom to User, which Option A accomplishes by using a WeakSet for the participants list.

Candidates often mistakenly believe that breaking the cycle from either side is equally effective, overlooking that the strong reference from ChatRoom to User is the one that must be weakened for User objects to be collected.

How to eliminate wrong answers

Option B is wrong because increasing the Python heap size does not resolve the underlying issue of circular references preventing garbage collection; it only delays the inevitable memory exhaustion. Option C is wrong because storing the `ChatRoom` reference in `User` using `weakref.ref` would break the cycle from the `User` side, but the `ChatRoom` still holds strong references to `User` objects in its participants list, so `User` objects would never become unreachable and would still leak. Option D is wrong because manually calling `gc.collect()` does not fix the root cause; the garbage collector can already collect cycles (by default), but if the `User` objects are still strongly referenced from the `ChatRoom` list, they are not garbage, and `gc.collect()` will not remove them.

Page 1 of 3

Page 2

All pages