PCAP-31-03: Comprehensions and I/O

Try 10 focused PCAP-31-03 questions on Comprehensions and I/O, with explanations, then continue with IT Mastery.

On this page

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

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

Topic snapshot

FieldDetail
Exam routePCAP-31-03
Topic areaSection 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)
Blueprint weight22%
Page purposeFocused sample questions before returning to mixed practice

How to use this topic drill

Use this page to isolate Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O) for PCAP-31-03. Work through the 10 questions first, then review the explanations and return to mixed practice in IT Mastery.

PassWhat to doWhat to record
First attemptAnswer without checking the explanation first.The fact, rule, calculation, or judgment point that controlled your answer.
ReviewRead the explanation even when you were correct.Why the best answer is stronger than the closest distractor.
RepairRepeat only missed or uncertain items after a short break.The pattern behind misses, not the answer letter.
TransferReturn to mixed practice once the topic feels stable.Whether the same skill holds up when the topic is no longer obvious.

Blueprint context: 22% of the practice outline. A focused topic score can overstate readiness if you recognize the pattern too quickly, so use it as repair work before timed mixed sets.

Sample questions

These questions are original IT Mastery practice items aligned to this topic area. They are designed for self-assessment and are not official exam questions.

Question 1

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

An intern tried to replace a working nested loop with a list comprehension, but the rewrite is broken. Which replacement line is the best fix if the goal is to keep exactly the same flat result list?

rows = [[1, 2, 3], [4], [5, 6]]

result = []
for row in rows:
    for n in row:
        if n % 2 == 0:
            result.append(n * 2)

# broken rewrite to replace
result = [n * 2 for n in rows for row in row if n % 2 == 0]

Options:

  • A. result = list(map(lambda row: row * 2, rows))

  • B. result = [[n * 2 for n in row if n % 2 == 0] for row in rows]

  • C. result = [n * 2 for row in rows for n in row if n % 2 == 0]

  • D. result = [n * 2 for n in row if n % 2 == 0 for row in rows]

Best answer: C

Explanation: A nested comprehension follows the same order as the equivalent nested for loops. The loop over rows must come before the loop over each n in row, and the filter must be applied to n.

The core idea is that nested comprehensions mirror nested loops from left to right. In the original code, Python first takes each row from rows, then takes each n from that row, then keeps only even n values and appends n * 2.

So the matching comprehension pattern is expression for outer in ... for inner in ... if condition. Here, row is the outer variable and n is the inner variable, so the correct structure is for row in rows followed by for n in row. That produces one flat list of doubled even numbers, just like the loop. A version with extra brackets changes the result into a nested list, and a map() call on row works on whole sublists instead of individual numbers.

  • The option starting with for n in row uses row before that variable is introduced, so the loop order is wrong.
  • The double-bracket option builds one inner list per row, changing the flat result into a nested list.
  • The map() option doubles each sublist as a list object and does not filter individual even numbers.

Question 2

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

During troubleshooting, a developer sees an unexpected result from this code:

nums = [0, 1, 2, 3, 4, 5]
result = [n * 2 for n in nums if n % 2]
print(result)

The program prints [2, 6, 10], but the developer expected doubled even numbers. What is the best cause?

Options:

  • A. The comprehension sorts odd results before printing them.

  • B. The if condition runs after n * 2 is computed.

  • C. List comprehensions skip 0, so even numbers are dropped.

  • D. n % 2 is truthy for odd values, so odds pass.

Best answer: D

Explanation: In a list comprehension, the if part filters source items before the expression is added to the new list. Here, n % 2 is nonzero for odd numbers, so odd values pass and are then doubled into [2, 6, 10].

The core concept is that the if clause in a list comprehension tests each original element first. Only elements with a truthy condition are used in the expression part.

With n % 2, even numbers produce 0, which is false, and odd numbers produce 1, which is true. That means 1, 3, and 5 pass the filter. After that, the expression n * 2 is applied to those values, producing 2, 6, and 10.

  • 0, 2, and 4 fail the condition.
  • 1, 3, and 5 pass the condition.
  • The multiplication happens only for passed values.

The key takeaway is that n % 2 selects odd numbers, not even ones.

  • The idea that filtering happens after multiplication fails because the if tests each original n before n * 2 is added.
  • The claim that list comprehensions skip 0 is false; 0 is excluded here only because the condition is false for it.
  • The sorting explanation fails because a list comprehension keeps the input order unless sorting is requested explicitly.

Question 3

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

Assume data.bin exists, open() succeeds, and the file contains exactly b"XYZ". What is printed by this code?

buf = bytearray(3)

try:
    f = open("data.bin", "rb")
    n = f.readinto(buf)
    print(n, buf.decode(), end="|")
    print(buf + "!")
except TypeError:
    print("TypeError")
finally:
    f.close()

Options:

  • A. 3 XYZ|bytearray(b’XYZ!')

  • B. 3 b’XYZ’|TypeError

  • C. 3 XYZ|TypeError

  • D. TypeError

Best answer: C

Explanation: readinto() fills the bytearray with the three bytes from the binary file. buf.decode() becomes XYZ, but buf + "!" is invalid because "!" is a str, so the TypeError handler runs after the first print().

Binary I/O works with bytes-like objects, and bytearray is one of them. In this code, readinto(buf) copies the file’s three bytes into the existing buffer, so n becomes 3 and buf.decode() returns the text XYZ. That makes the first output 3 XYZ|.

  • print(buf + "!") must evaluate buf + "!" before printing anything.
  • buf is a bytearray, but "!" is a text string.
  • Python does not implicitly combine text and byte-oriented data here, so a TypeError is raised.

Using b"!" instead of "!" would make the concatenation valid.

  • The option showing bytearray(b'XYZ!') assumes Python auto-converts a text string to bytes, which it does not.
  • The option showing b'XYZ' after decode() is wrong because decode() returns a normal text string.
  • The option with only TypeError ignores that the first print() completes before the invalid concatenation is attempted.

Question 4

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

A developer expects each returned function to keep the loop value from the iteration in which it was created.

def build_adders():
    funcs = []
    for rate in [10, 20, 30]:
        def add(x):
            return x + rate
        funcs.append(add)
    return funcs

a, b, c = build_adders()
print(a(1), b(1), c(1))

What is printed?

Options:

  • A. It raises NameError

  • B. It prints 31 31 31

  • C. It prints 11 21 31

  • D. It prints 11 11 11

Best answer: B

Explanation: This is a closure late-binding issue. The inner function uses the outer name rate, and that name is resolved when each returned function is called, not when it is created, so all three calls use 30.

In Python, a closure keeps access to variables from an enclosing scope, but it does not automatically store a separate snapshot of a loop variable for each iteration. Here, each add function closes over the same outer name rate.

When a(1), b(1), and c(1) are called, the loop in build_adders() has already finished, and rate has its final value, 30. So every function computes 1 + 30 and returns 31.

If the goal is to keep the current loop value at creation time, a common fix is to bind it as a default argument, such as def add(x, rate=rate):. The tempting 11 21 31 result matches the caller’s expectation, but not Python’s actual closure behavior.

  • The option with 11 21 31 would be correct only if each function stored a separate value from its own loop iteration.
  • The option with 11 11 11 assumes the first loop value was frozen for all closures, which this code does not do.
  • The NameError option is incorrect because the inner function still has access to rate through the closure.

Question 5

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

A developer is validating a binary file header. The file begins with the ASCII bytes b"PC", and the code should keep using a bytearray buffer and binary I/O. The current snippet fails:

with open("tag.bin", "rb") as f:
    buf = bytearray(2)
    f.readinto(buf)

if buf.startswith("PC"):
    print("tag found")

What is the best fix?

Options:

  • A. Change the test to if buf.decode().startswith(b"PC"):

  • B. Change the test to if buf.decode.startswith("PC"):

  • C. Open the file with "r" instead of "rb".

  • D. Change the test to if buf.startswith(b"PC"):

Best answer: D

Explanation: bytearray holds raw byte values, not text characters. In a binary workflow, startswith() must receive bytes-like data, so using b"PC" is the correct fix.

The core issue is mixing binary data with text data. open(..., "rb") reads raw bytes, and readinto(buf) fills the existing bytearray buffer with those bytes. Because buf is a binary buffer, methods such as startswith() expect a bytes-like argument such as bytes or bytearray, not a Python str.

Using b"PC" keeps the comparison in binary form and matches the data already stored in the buffer. If you wanted to work with text instead, you would first decode the bytes and then compare to a normal string, but that would be a different approach from the binary-buffer design shown here.

The key takeaway is: binary buffers compare to bytes-like objects, not to text strings.

  • Opening the file in text mode breaks the binary-buffer approach; readinto() is meant for binary streams and bytes-like buffers.
  • Using buf.decode.startswith("PC") targets the decode method itself instead of calling it.
  • Decoding first and then checking with b"PC" still mixes text and bytes, because decoded text should be compared to a normal string.

Question 6

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

A developer is filtering mixed input before numeric processing. The code must keep only positive integers from data and must not enter the except block.

data = [3, "5", -2, 8, "x", 0]

try:
    kept = list(filter(____, data))
except TypeError as ex:
    print("type error")
else:
    print(kept)
finally:
    print("done")

Which replacement for ____ meets the requirement?

Options:

  • A. lambda x: x.isdigit() and int(x) > 0

  • B. lambda x: isinstance(x, int) and x > 0

  • C. lambda x: x > 0

  • D. lambda x: int(x) > 0

Best answer: B

Explanation: filter() keeps elements whose predicate returns True. With mixed types, the safe predicate must test whether a value is an integer before applying x > 0, so the filtering finishes normally and the else block runs.

filter(function, iterable) applies the predicate to each element and retains only the elements for which the result is truthy. Here, data contains both int and str values, so the predicate must be safe for both types.

  • isinstance(x, int) checks whether the current element is an integer.
  • and short-circuits, so x > 0 is evaluated only when that first test is True.
  • Non-integer values are simply rejected, not converted or compared.

That means the filtered result is [3, 8], no exception is raised, the except TypeError as ex block is skipped, and the else block executes. Predicates that compare or convert the wrong type can raise exceptions before filtering completes.

  • Direct comparison fails because comparing a string such as "5" to 0 raises TypeError.
  • Forced conversion fails because int("x") raises ValueError, which this handler does not catch.
  • String-only method fails because integers like 3 do not have an .isdigit() method.

Question 7

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

A developer is troubleshooting writer.py. The file should contain b'OKPY', but this code raises TypeError: a bytes-like object is required, not 'str':

header = 'OK'
payload = bytearray(b'PY')

with open('result.bin', 'wb') as f:
    f.write(header)
    f.write(payload)

Which change correctly fixes the problem while keeping binary I/O?

Options:

  • A. Change open('result.bin', 'wb') to open('result.bin', 'w')

  • B. Replace header with str(header) in the first write() call

  • C. Replace f.write(payload) with f.write(payload.decode('utf-8'))

  • D. Replace f.write(header) with f.write(header.encode('utf-8'))

Best answer: D

Explanation: A file opened with wb expects bytes-like data, not a Python str. The bytearray payload is already valid for binary output, so the fix is to encode only the text header before writing it.

The core concept is the difference between text and binary data in file I/O. In binary mode ('wb'), write() accepts bytes-like objects such as bytes and bytearray. Here, payload is already a bytearray, so it is fine. The problem is header, which is a str; that value must be converted to bytes first.

Using header.encode('utf-8') turns 'OK' into b'OK', so both writes are valid and the file receives the expected byte sequence. Switching to text mode would only move the problem, because a text stream accepts strings but not a raw bytearray. The key takeaway is: encode text before writing to a binary stream.

  • Decode payload changes the buffer into a string, which is still the wrong type for a binary stream and does not fix the first failing write.
  • Text mode would accept the header string, but the bytearray payload would then be invalid for that stream.
  • str(header) does nothing useful here because header is already a string.

Question 8

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

A developer needs result to contain the lengths of words longer than 3 characters, preserving order. The current code returns the wrong kind of values.

words = ["to", "code", "learn", "AI"]
result = list(map(lambda w: len(w) > 3, words))

Which two replacements correctly fix the code? Select TWO.

Options:

  • A. result = list(filter(lambda n: n, map(len, words)))

  • B. result = list(filter(lambda w: len(w) > 3, words))

  • C. result = list(map(len, filter(lambda w: len(w) > 3, words)))

  • D. result = list(filter(lambda n: n > 3, map(len, words)))

  • E. result = list(map(len, words))

  • F. result = list(map(lambda w: len(w) > 3, filter(lambda w: len(w), words)))

Correct answers: C and D

Explanation: map() transforms every input item, while filter() selects only items that satisfy a condition. Here, the task needs both selection and transformation, so either filter the words and then map to lengths, or map to lengths and then filter numeric values over 3.

The bug is that map(lambda w: len(w) > 3, words) applies a test to every word and returns booleans, not the required lengths. Use filter() when the lambda answers a yes/no question about whether an item should stay, and use map() when the lambda converts an item into a new value.

For this requirement, two valid patterns exist:

  • Filter words by len(w) > 3, then map those remaining words to len.
  • Map all words to their lengths, then filter lengths greater than 3.

Both produce [4, 5] for the given list. The closest wrong idea is filtering the original words only, because that keeps 'code' and 'learn' instead of converting them to 4 and 5.

  • Filtered words only keeps the matching strings, not their lengths.
  • Boolean mapping still returns True/False values because every word is transformed by the predicate.
  • Truthiness filter on numeric lengths keeps all nonzero lengths, so 2 values stay too.
  • Map only converts every word to a length because no filtering step happens.

Question 9

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

Assume in.bin exists and out.txt can be created. What is the exact output of this program?

try:
    with open("in.bin", "rb") as src, open("out.txt", "wt", encoding="utf-8") as dst:
        chunk = src.readline()
        dst.write(chunk)
except TypeError as ex:
    print(type(chunk).__name__, type(ex).__name__)
else:
    print("done")
finally:
    print("closed")

Options:

  • A. done then closed

  • B. bytes TypeError then closed

  • C. bytes UnsupportedOperation then closed

  • D. str TypeError then closed

Best answer: B

Explanation: A file opened with rb returns bytes from readline(), while a file opened with wt expects str in write(). That mismatch raises TypeError, the except block prints the type names, and finally always prints closed.

The key distinction is that binary mode works with bytes, while text mode works with str. Here, src.readline() is called on a file opened with rb, so chunk is a bytes object. The destination file is opened with wt, and write() in text mode expects a string, not bytes.

Because of that mismatch, dst.write(chunk) raises TypeError. The except TypeError as ex block handles it and prints bytes TypeError. The else block is skipped because an exception occurred, and the finally block still executes, printing closed.

The closest wrong idea is that text mode would automatically convert bytes to text, but Python does not do that implicit conversion here.

  • The option claiming done then closed fails because text-mode write() does not implicitly convert bytes to str.
  • The option claiming str TypeError then closed fails because readline() from a file opened with rb returns bytes, not str.
  • The option claiming bytes UnsupportedOperation then closed fails because the file is writable; the problem is the data type, not the file mode capability.

Question 10

Topic: Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O)

Assume notes.txt contains exactly:

alpha
beta
gamma

There is a newline after alpha and beta, but not after gamma.

f = open("notes.txt", "r")
f.readline()
result = f.readlines()
f.close()
print(result)

What is printed?

Options:

  • A. 'beta\ngamma'

  • B. ['alpha\n', 'beta\n', 'gamma']

  • C. ['beta\n', 'gamma']

  • D. ['beta', 'gamma']

Best answer: C

Explanation: readline() reads and consumes the first line, so the file position moves to the start of beta. Then readlines() returns a list of the remaining lines, keeping \n on lines that actually end with a newline.

readline() returns one line from the current file position and advances the file cursor past that line. After the first call, the cursor is already at the start of beta, so readlines() collects only the remaining lines into a list. In text mode, each line keeps its trailing newline if the file contains one.

  • The first call consumes alpha\n.
  • The second call returns the rest as a list.
  • beta keeps \n, but gamma does not because the file has no final newline.

So the printed value is a two-element list, not the whole file and not one combined string.

  • The option omitting \n is wrong because readlines() preserves line endings in text mode.
  • The option including alpha\n ignores that the first readline() already consumed that line.
  • The single-string option confuses readlines() with read(), which would return one string instead of a list.

Continue with full practice

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

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

Free review resource

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

Revised on Thursday, May 14, 2026