Free Python Institute PCAP Practice Questions: Comprehensions and I/O
Practice 10 free Python Institute PCAP - Certified Associate Python Programmer (PCAP-31-03) questions on Comprehensions and I/O, with answers, explanations, and the IT Mastery next step.
Try the IT Mastery web app for a richer interactive practice experience with mixed sets, timed mocks, topic drills, explanations, and progress tracking.
Topic snapshot
| Field | Detail |
|---|---|
| Practice target | Python Institute PCAP |
| Topic area | Section 5: Miscellaneous (List Comprehensions, Lambdas, Closures, I/O) |
| Blueprint weight | 22% |
| Page purpose | Focused 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 Python Institute PCAP. Work through the 10 questions first, then review the explanations and return to mixed practice in IT Mastery.
| Pass | What to do | What to record |
|---|---|---|
| First attempt | Answer without checking the explanation first. | The fact, rule, calculation, or judgment point that controlled your answer. |
| Review | Read the explanation even when you were correct. | Why the best answer is stronger than the closest distractor. |
| Repair | Repeat only missed or uncertain items after a short break. | The pattern behind misses, not the answer letter. |
| Transfer | Return to mixed practice once the topic feels stable. | Whether the same skill holds up when the topic is no longer obvious. |
Blueprint context: 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 are original IT Mastery practice questions aligned to this topic area. They are not official Python Institute questions, copied live-exam content, or exam dumps. Use them to preview question style and explanation depth before continuing with topic drills, mixed sets, and timed mocks in IT Mastery.
Question 1
Topic: Section 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 rowusesrowbefore 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
ifcondition runs aftern * 2is computed.C. List comprehensions skip
0, so even numbers are dropped.D.
n % 2is 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, and4fail the condition.1,3, and5pass 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
iftests each originalnbeforen * 2is added. - The claim that list comprehensions skip
0is false;0is 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 evaluatebuf + "!"before printing anything.bufis abytearray, but"!"is a text string.- Python does not implicitly combine text and byte-oriented data here, so a
TypeErroris 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'afterdecode()is wrong becausedecode()returns a normal text string. - The option with only
TypeErrorignores that the firstprint()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
NameErrorB. It prints
31 31 31C. It prints
11 21 31D. 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 31would be correct only if each function stored a separate value from its own loop iteration. - The option with
11 11 11assumes the first loop value was frozen for all closures, which this code does not do. - The
NameErroroption is incorrect because the inner function still has access toratethrough 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 thedecodemethod 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) > 0B.
lambda x: isinstance(x, int) and x > 0C.
lambda x: x > 0D.
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.andshort-circuits, sox > 0is evaluated only when that first test isTrue.- 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"to0raisesTypeError. - Forced conversion fails because
int("x")raisesValueError, which this handler does not catch. - String-only method fails because integers like
3do 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')toopen('result.bin', 'w')B. Replace
headerwithstr(header)in the firstwrite()callC. Replace
f.write(payload)withf.write(payload.decode('utf-8'))D. Replace
f.write(header)withf.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
bytearraypayload would then be invalid for that stream. str(header)does nothing useful here becauseheaderis 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 tolen. - 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/Falsevalues because every word is transformed by the predicate. - Truthiness filter on numeric lengths keeps all nonzero lengths, so
2values 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.
donethenclosedB.
bytes TypeErrorthenclosedC.
bytes UnsupportedOperationthenclosedD.
str TypeErrorthenclosed
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
donethenclosedfails because text-modewrite()does not implicitly convertbytestostr. - The option claiming
str TypeErrorthenclosedfails becausereadline()from a file opened withrbreturnsbytes, notstr. - The option claiming
bytes UnsupportedOperationthenclosedfails 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.
betakeeps\n, butgammadoes 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
\nis wrong becausereadlines()preserves line endings in text mode. - The option including
alpha\nignores that the firstreadline()already consumed that line. - The single-string option confuses
readlines()withread(), which would return one string instead of a list.
Continue in the web app
Use IT Mastery for interactive Python Institute PCAP practice with mixed sets, timed mocks, topic drills, explanations, and progress tracking.
Try Python Institute PCAP on Web