Free PCEP-30-02 Full-Length Practice Exam: 30 Questions

Try 30 free PCEP-30-02 questions across the exam domains, with explanations, then continue with full IT Mastery practice.

This free full-length PCEP-30-02 practice exam includes 30 original IT Mastery questions across the exam domains.

These questions are for self-assessment. They are not official exam questions and do not imply affiliation with the exam sponsor.

Count note: this page uses the full-length practice count maintained in the Mastery exam catalog. Some certification vendors publish total questions, scored questions, duration, or unscored/pretest-item rules differently; always confirm exam-day rules with the sponsor.

Need concept review first? Read the PCEP-30-02 Cheat Sheet on Tech Exam Lexicon, then return here for timed mocks and full IT Mastery practice.

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

Try PCEP-30-02 on Web View full PCEP-30-02 practice page

Exam snapshot

  • Exam route: PCEP-30-02
  • Practice-set question count: 30
  • Time limit: 40 minutes
  • Practice style: mixed-domain diagnostic run with answer explanations

Full-length exam mix

DomainWeight
Block 1: Computer Programming and Python Fundamentals18%
Block 2: Control Flow - Conditional Blocks and Loops29%
Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings25%
Block 4: Functions and Exceptions28%

Use this as one diagnostic run. IT Mastery gives you timed mocks, topic drills, analytics, code-reading practice where relevant, and full practice.

Practice questions

Questions 1-25

Question 1

Topic: Block 4: Functions and Exceptions

A beginner wants this program to print Hello Mia three times, but the last line fails.

def repeat_msg(name, count):
    for _ in range(count):
        print("Hello", name)

repeat_msg("Mia")

Without changing the function definition, which change best fixes the program?

Options:

  • A. repeat_msg(name="Mia")

  • B. repeat_msg(3, "Mia")

  • C. repeat_msg("Mia", 3)

  • D. repeat_msg()

Best answer: C

Explanation: repeat_msg has two required parameters: name and count. The call currently passes only one value, so the fix is to supply the missing second argument, such as 3.

In Python, a function call must provide a value for every required parameter unless that parameter has a default value. Here, repeat_msg(name, count) defines two required parameters, and neither has a default. That means repeat_msg("Mia") raises a TypeError because count is missing.

The program is supposed to print the same greeting three times, so the call needs both pieces of information:

  • the name: "Mia"
  • the repeat count: 3

So the working call is repeat_msg("Mia", 3). Calls that still omit count remain invalid, and swapping the argument order sends the wrong values into the parameters. Match the function call to the function definition exactly.

  • The option with 3 first swaps the values, so count receives "Mia", which breaks range(count).
  • The option using only name="Mia" still leaves the required count argument missing.
  • The empty call is also invalid because it omits both required arguments.

Question 2

Topic: Block 4: Functions and Exceptions

What happens when this Python 3 code runs?

def show(message):
    print(message)

word = "Hi"
show(word, "!")

Options:

  • A. It raises TypeError

  • B. It prints Hi!

  • C. It raises NameError

  • D. It prints Hi

Best answer: A

Explanation: message is the only parameter in the function definition, so show() accepts one positional input. The call supplies two arguments, word and "!", so Python raises TypeError before the function body runs.

A parameter is the name listed in a function definition, and an argument is the actual value or expression supplied in the function call. In def show(message):, message is the single parameter, so the function expects exactly one positional argument.

In show(word, "!"), Python supplies two arguments:

  • the value stored in word
  • the string "!"

Because the number of arguments does not match the number of parameters, Python stops with a TypeError. The print(message) line is never executed.

A common mistake is to think the function will print both values, but argument checking happens before the function body starts.

  • Expecting Hi! fails because show() is rejected before it can print anything.
  • Expecting Hi fails because Python does not ignore the extra argument in a normal function call.
  • Expecting NameError fails because word is defined; the problem is the argument count.

Question 3

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A student wants this program to print Not found only when target is absent from the list. With the current code, nothing is printed for the shown values. What is the best fix?

numbers = [1, 3, 5]
target = 4

for n in numbers:
    if n != target:
        break
else:
    print("Not found")

Options:

  • A. Replace break with continue

  • B. Delete the else and print after the loop

  • C. Move print("Not found") inside the loop

  • D. Change the condition to if n == target:

Best answer: D

Explanation: In Python, a loop else block runs only if the loop finishes without break. The current code breaks on the first non-matching value, so the search stops too early and the else block is skipped.

The core concept is Python’s for ... else behavior: the else block is tied to the loop, and it runs only when the loop ends normally. If break executes, the else block does not run.

Here, if n != target: is backwards for a search. Since the first value (1) is not equal to 4, the loop breaks immediately. The loop never finishes checking the whole list, so print("Not found") is skipped.

for n in numbers:
    if n == target:
        break
else:
    print("Not found")

This pattern searches the list and uses else only when no match was found.

  • Replacing break with continue keeps the loop running, but the else block would still run even if the target exists.
  • Printing Not found inside the loop can happen after the first non-match, before all items are checked.
  • Printing after the loop would show Not found every time, including when a match was found.

Question 4

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

A student wants this program to print bAnana by changing only the character at index 1 and keeping text as a string:

text = "banana"
text[1] = "A"
print(text)

Which replacement for the broken assignment line is the best fix?

Options:

  • A. text = text[:1] + "A" + text[2:]

  • B. text = "A" + text[1:]

  • C. text[1:2] = "A"

  • D. text = text.replace("a", "A")

Best answer: A

Explanation: Strings are immutable in Python, so you cannot assign directly to a character position. The correct fix creates a new string by joining the part before index 1, the new character, and the part after index 1.

A Python string cannot be changed in place after it is created. That is why assigning to text[1] fails: indexing lets you read a character, but not overwrite it. To change one character, you must build a new string.

For this case:

  • text[:1] gives "b"
  • "A" is the replacement character
  • text[2:] gives "nana"

Combining those parts produces "bAnana". This keeps text as a string and changes only the character at the required position. A tempting alternative is using replace(), but that works by matching values, not by targeting one index.

  • replace("a", "A") changes every matching a, so it does not target only index 1.
  • "A" + text[1:] replaces the first character, not the second one.
  • Assigning to text[1:2] still tries to modify the existing string, which is not allowed.

Question 5

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A student wants this program to print 3, 2, 1 and then stop, but it keeps printing larger numbers and never ends. What is the best cause of the problem?

count = 3

while count > 0:
    print(count)
    count += 1

Options:

  • A. count += 1 moves count away from 0, so the condition stays true.

  • B. The loop needs a break statement after print(count).

  • C. print(count) should be outside the loop.

  • D. The condition should be count >= 0.

Best answer: A

Explanation: The loop condition is count > 0, but count starts at 3 and is increased each time. Tracing the values gives 3, 4, 5, and so on, so the condition never becomes false and the loop is infinite.

To trace a while loop, follow the control variable from its initial value through each update. Here, count starts at 3, and the loop runs while count > 0. Inside the loop, count += 1 makes the value larger, not smaller, so every new value still satisfies the condition.

  • Start: count is 3, so the loop begins.
  • After one pass: count becomes 4.
  • After another pass: count becomes 5.
  • The same pattern continues indefinitely.

A while loop stops only when its condition becomes false. If the goal is to count down to 0, the update must move toward 0, such as count -= 1.

  • Changing the condition to count >= 0 does not help, because 3, 4, 5, and later values still satisfy it.
  • Moving print(count) outside the loop changes the output behavior, but it does not fix the loop control problem.
  • Adding break could force an exit, but it does not identify the actual bug in the loop update.

Question 6

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

A ticket-check program stores valid ticket IDs in a list.

ticket_id = "B12"
valid_ids = ["A10", "B12", "C07"]

Which TWO expressions correctly evaluate to True when ticket_id is present in valid_ids? Select TWO.

Options:

  • A. ticket_id in valid_ids

  • B. ticket_id not in valid_ids

  • C. valid_ids in ticket_id

  • D. ticket_id == valid_ids

  • E. valid_ids[ticket_id]

  • F. bool(ticket_id in valid_ids)

Correct answers: A and F

Explanation: Python uses in to check whether a value appears as an element in a list. Both the plain membership expression and the same expression wrapped in bool() correctly return True here because "B12" is one of the items in valid_ids.

The core concept is list membership testing with in. In Python, the searched value goes on the left and the list goes on the right: value in some_list. That expression already evaluates to a Boolean value.

In this scenario, ticket_id is "B12", and valid_ids contains "B12", so the membership test is True.

  • ticket_id in valid_ids is the standard membership check.
  • bool(ticket_id in valid_ids) is also correct because it converts an already Boolean result to True or False.

Common mistakes are reversing the operands, comparing a single value to the whole list, or using not in, which checks the opposite condition.

  • Reversed operands fails because the value should be checked inside the list, not the list inside the string.
  • Whole-list comparison fails because comparing one string to an entire list does not test membership.
  • Opposite condition fails because not in is True only when the value is absent.
  • Index confusion fails because list indexing uses integer positions, not a membership test string.

Question 7

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A student is drafting a function and wants to leave one branch unfinished for now. The code must stay valid, and the later print() and return lines should still run normally.

def check_age(age):
    if age < 0:
        # validation will be added later
        ???
    print("checked")
    return age

Which statement should replace ????

Options:

  • A. continue

  • B. pass

  • C. return

  • D. break

Best answer: B

Explanation: pass is Python’s placeholder statement for an empty block. It satisfies the if body requirement without exiting the function or changing what runs next.

Python requires an indented statement after an if. When you want that block to exist for now but do nothing, use pass. In this function, pass lets the if age < 0 branch stay empty while control flow continues normally to print("checked") and then return age.

  • pass does nothing.
  • It keeps the code syntactically correct.
  • It does not stop the function or skip later statements.

The closest wrong choice is return, because it is valid inside a function, but it would end the function early instead of acting as a placeholder.

  • return changes the function’s flow by exiting immediately.
  • continue can be used only inside a loop, not in this standalone if block.
  • break also requires a loop and cannot be used here.

Question 8

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A student wants to count all (row, col) pairs in a 3 x 2 grid, but the update line is indented as shown.

count = 0

for row in range(3):
    for col in range(2):
        print(row, col)
    count += 1

print("count =", count)

What is the final line printed?

Options:

  • A. It raises IndentationError.

  • B. It prints count = 3.

  • C. It prints count = 6.

  • D. It prints count = 2.

Best answer: B

Explanation: Indentation decides which loop controls a statement. Here, count += 1 is outside the inner loop but inside the outer loop, so it runs once for each row. Since there are 3 outer iterations, the final line is count = 3.

In nested loops, a line runs only in the block where it is indented. The inner loop here contains only print(row, col). The line count += 1 is aligned with the inner for, so it is not part of the inner loop body.

That means this happens:

  • the inner loop prints two (row, col) pairs for one row
  • then count increases by 1
  • this repeats for 3 rows

So count changes from 0 to 3. To get 6, the increment would need to be indented one level deeper so it runs for every (row, col) pair.

  • The option claiming count = 6 assumes the increment is inside the inner loop.
  • The option claiming count = 2 confuses the number of columns with the number of times the update runs.
  • The option claiming IndentationError is incorrect because the code is indented validly; the problem is logical, not syntactic.

Question 9

Topic: Block 4: Functions and Exceptions

A beginner is troubleshooting why this program does not end with an uncaught exception. What is the output?

def calc():
    return 10 / 0

try:
    calc()
    print("try")
except ZeroDivisionError:
    print("except")

print("end")

Options:

  • A. It ends with an uncaught ZeroDivisionError.

  • B. It prints only except.

  • C. It prints except and then end.

  • D. It prints try and then end.

Best answer: C

Explanation: The function call raises ZeroDivisionError inside the caller’s try block. That exception is caught by the matching except, so print("try") is skipped and execution continues to the final print("end").

When calc() runs, 10 / 0 raises ZeroDivisionError. Because that call happens inside the try block, control immediately leaves the rest of the try block, so print("try") never runs.

The caller has a matching except ZeroDivisionError, so the exception is handled there and print("except") runs. After the exception is handled, the program continues normally with the next statement after the try/except, so it also prints end.

An uncaught traceback would appear only if there were no matching handler in the caller.

  • The option claiming try is printed fails because execution stops the try block as soon as calc() raises.
  • The option claiming only except is printed fails because the program continues after a handled exception.
  • The traceback option fails because the caller explicitly catches ZeroDivisionError.

Question 10

Topic: Block 2: Control Flow - Conditional Blocks and Loops

What is printed by this code?

def check(values):
    for v in values:
        if v < 0:
            break
    else:
        return "all non-negative"
    return "negative found"

print(check([3, 1, 0]))

Options:

  • A. Nothing is printed

  • B. all non-negative

  • C. None

  • D. negative found

Best answer: B

Explanation: In Python, a loop else runs only when the loop finishes normally, not when it is ended by break. Here, no value in [3, 1, 0] is negative, so the function returns all non-negative and print() displays it.

The key concept is that a loop else belongs to the for loop, not to the if statement inside it. Python executes the loop else only if the loop completes all iterations without hitting break.

In this function, the values are 3, 1, and 0. For each value, the condition v < 0 is checked, and it is false every time. Because no break happens, the for loop finishes normally, so the else block runs and returns "all non-negative".

That returned string is then passed to print(). The common mistake is to think the else is paired with if, but here it is paired with the loop.

  • The option with negative found fails because that return happens only after a break, and no negative value appears.
  • The option with None fails because both possible paths in the function return a string.
  • The option claiming nothing is printed fails because print() always displays the function’s returned string here.

Question 11

Topic: Block 1: Computer Programming and Python Fundamentals

A beginner is testing a console checkout script. After the user enters data, the program has:

is_member = True
has_coupon = False
is_guest = False

Which TWO if conditions evaluate to True in Python?

Options:

  • A. is_member and not has_coupon or is_guest

  • B. not is_guest or has_coupon and not is_member

  • C. not is_member and has_coupon or is_guest

  • D. not is_member or has_coupon or is_guest

  • E. is_guest or has_coupon and is_member

  • F. not is_guest and has_coupon or not is_member

Correct answers: A and B

Explanation: Python evaluates logical operators in this order: not, then and, then or. Using is_member = True, has_coupon = False, and is_guest = False, only two expressions reduce to True after applying that precedence.

The key concept is Python’s logical operator precedence: not is evaluated first, then and, and finally or. That means you should simplify each expression in that order instead of reading straight from left to right.

  • is_member and not has_coupon or is_guest becomes True and True or False, then True or False, so it is True.
  • not is_guest or has_coupon and not is_member becomes True or False and False, then True or False, so it is True.

All other options reduce to False because their and parts or remaining or terms are false. A common mistake is to treat or as if it were checked before and, but Python does the opposite.

  • The option with not is_member and has_coupon or is_guest fails because each part becomes False.
  • The option with not is_guest and has_coupon or not is_member fails because True and False becomes False, and not is_member is also False.
  • The option with is_guest or has_coupon and is_member fails because has_coupon is False, so both sides of or are false.
  • The option with not is_member or has_coupon or is_guest fails because all three terms evaluate to False.

Question 12

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

A student wants to label a collection as editable only when its elements can be changed in place.

collections = [(1, 2), [3, 4]]

for data in collections:
    if type(data) == list:
        print("editable")
    else:
        print("fixed")

Which statement correctly describes this control flow?

Options:

  • A. It raises an error because a for loop cannot iterate over a tuple and a list in one outer list.

  • B. It prints fixed then editable because tuples are immutable and lists are mutable.

  • C. It prints editable twice because both collections can store multiple values.

  • D. It prints fixed twice because both collections are sequences.

Best answer: B

Explanation: The for loop checks each collection one at a time. A tuple is not a list, so it prints fixed first; the second item is a list, so it prints editable next.

This question combines simple control flow with the difference between tuples and lists. The loop visits the first item, (1, 2), and the condition type(data) == list is false, so the else branch prints fixed. Then it visits the second item, [3, 4], and the condition is true, so the if branch prints editable.

The important collection concept is that tuples are immutable, meaning their elements cannot be reassigned after creation. Lists are mutable, so their elements can be changed in place. Being a sequence or holding multiple values does not make a collection mutable.

The key takeaway is that both tuples and lists can be looped over, but only lists are meant for later modification.

  • Multiple values is not enough; both tuples and lists can store several items, but only lists are mutable.
  • Both are sequences is true, but sequence type does not mean both follow the same branch.
  • Loop error is incorrect because a for loop can iterate over an outer list containing different collection types.

Question 13

Topic: Block 4: Functions and Exceptions

What is the output of this Python 3 code?

price = 20

def show_price():
    price = 15
    print(price)

show_price()
print(price)

Options:

  • A. First 20, then 20

  • B. It raises an error because price is defined twice

  • C. First 15, then 20

  • D. First 15, then 15

Best answer: C

Explanation: A variable assigned inside a function is local unless the function uses global. Here, the function prints its own local price, but the variable outside the function keeps its original value.

This code demonstrates local scope and shadowing. The name price exists outside the function with value 20, but price = 15 inside show_price() creates a separate local variable for that function call. That local variable is used only inside the function body, so the first print(price) shows 15.

After the function finishes, the local variable disappears. The outer price was never changed, so the final print(price) shows 20.

The closest wrong idea is that assigning price inside the function also updates the outer variable, but that would require using global.

  • The choice with 15 twice assumes the assignment inside the function changes the outer variable, which it does not.
  • The choice with 20 twice ignores that the function prints its own local price.
  • The error choice fails because Python allows a local variable to shadow a variable defined outside the function.

Question 14

Topic: Block 1: Computer Programming and Python Fundamentals

What is the exact console output of this Python 3 code?

print("A", "B", sep=":", end="*")
print("C", "D", sep="-", end="!")

Options:

  • A. A:B*C D!

  • B. A:B* C-D!

  • C. A B*C-D!

  • D. A:B*C-D!

Best answer: D

Explanation: The first print() joins A and B with : and ends with * instead of a newline. The second print() continues on the same line, joins C and D with -, and ends with !, so the display is A:B*C-D!.

print() has two optional arguments that matter here: sep changes the text placed between multiple values, and end changes what is printed after them. In the first call, sep=":" makes the output A:B, and end="*" adds * instead of the usual newline. Because no newline is printed, the next print() starts immediately after that *.

In the second call, sep="-" makes C-D, and end="!" adds the final exclamation mark. Putting both calls together produces A:B*C-D!.

A common mistake is to think end adds its text and then still moves to a new line, but it replaces the newline.

  • The two-line option assumes the first print() still moves to a new line after printing *.
  • The option showing A B ignores sep=":" in the first print().
  • The option showing C D ignores sep="-" in the second print().

Question 15

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

The program should add a new key-value pair to the user dictionary, but it uses the wrong operation. Which replacement line is the best fix?

user = {"name": "Lena", "age": 19}
user.append("city", "Paris")
print(user)

Options:

  • A. user["city"] = "Paris"

  • B. user.update("city", "Paris")

  • C. user.add("city", "Paris")

  • D. user.insert("city", "Paris")

Best answer: A

Explanation: Dictionaries add one new entry by key assignment with square brackets. Using user["city"] = "Paris" creates the city key and stores its value in the existing dictionary.

The core concept is dictionary mutation by item assignment. In Python, the normal way to add one key-value pair is dictionary[key] = value. If the key does not exist yet, Python creates it; if it already exists, Python updates its value.

In this program, append() is the problem because that method is used with lists, not dictionaries. Replacing that line with user["city"] = "Paris" correctly adds a new entry to the dictionary without changing the rest of the code.

This is the simplest and most direct fix for adding exactly one new key-value pair.

  • add() is not the method used to add a single key-value pair to a dictionary.
  • insert() is associated with list-style position-based insertion, not dictionary keys.
  • update("city", "Paris") uses a real dictionary method name, but the arguments are in the wrong form for update().

Question 16

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A student wants to rewrite this loop so it uses sequence iteration because item positions are never needed. The program should print each code until STOP is found, then stop.

codes = ["A12", "B34", "STOP", "C56"]

for i in range(len(codes)):
    if codes[i] == "STOP":
        break
    print(codes[i])

Which change is correct?

Options:

  • A. Use for i in codes, then test codes[i] against STOP before printing.

  • B. Use for i in range(len(codes)), but test i against STOP before printing.

  • C. Use for code in codes, compare code to STOP, and break when it matches.

  • D. Use for code in codes, but replace break with continue when code is STOP.

Best answer: C

Explanation: The loop only needs each list value, not its position, so direct iteration is the right control flow. Iterating with for code in codes keeps the same behavior: print each code in order and stop immediately when STOP is reached.

When indexes are not needed, Python code should usually iterate over the sequence elements directly with for item in sequence. In this case, the task is to examine each code value, print it, and stop when the sentinel value STOP appears. That means the loop variable should hold each string from the list, not a numeric position.

  • for code in codes gives each code directly.
  • break ends the loop as soon as STOP is found.
  • range(len(codes)) is unnecessary here because the index is never used for a separate purpose.

The closest distractor changes break to continue, but that would skip STOP and keep processing later items.

  • Using for i in codes with codes[i] fails because the loop variable would be a code string, not a valid list index.
  • Replacing break with continue changes the behavior from stopping at STOP to skipping it and continuing.
  • Comparing the index from range(len(codes)) to STOP fails because the index is an integer, not a list element.

Question 17

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A student runs this script:

total = 0

for i in range(1, 5):
    if i == 3:
        break
    total += i
else:
    total += 10

print(total)

What is printed?

Options:

  • A. It prints 13

  • B. It prints 16

  • C. It prints 3

  • D. It prints 6

Best answer: C

Explanation: The script prints 3. The loop adds 1 and 2, then stops when i becomes 3. In a for loop, the else block runs only if the loop finishes without break, so it does not run here.

In a for loop, break immediately ends the loop, and a loop else runs only when the loop completes all iterations normally. Here, range(1, 5) produces 1, 2, 3, and 4. The variable total starts at 0, becomes 1 after the first pass, and 3 after the second pass. On the next pass, i is 3, so the if i == 3 condition is true and break executes before total += i can run. Because the loop ended with break, the else block is skipped, so 10 is not added. The final printed value is 3. The main trap is treating loop else like a normal if/else; it depends on whether the loop was broken early.

  • The 6 choice assumes 3 is added before break, but break happens first.
  • The 13 choice assumes the loop else always runs, but break prevents it.
  • The 16 choice combines both mistakes by adding 3 and also adding 10 from the skipped else block.

Question 18

Topic: Block 4: Functions and Exceptions

A beginner is writing helper functions for a console app. Which TWO headers could start a valid Python function definition?

Options:

  • A. function greet():

  • B. def class():

  • C. def 2greet():

  • D. greet def():

  • E. def print_total(value):

  • F. def greet():

Correct answers: E and F

Explanation: A valid Python function header uses def, then a legal identifier, then parentheses and a colon. greet and print_total follow those rules, so both can begin user-defined function definitions.

In Python, a user-defined function starts with a header in the form def name(parameters):. The function name must be a valid identifier: it may use letters, digits, and underscores, but it cannot start with a digit and it cannot be a reserved keyword such as class. Both greet and print_total satisfy those naming rules, and both headers use the required def keyword, parentheses, and trailing colon. Options that use a different keyword, an illegal name, or the wrong word order do not match Python syntax. The key check is simple: correct def syntax plus a legal function name.

  • Wrong keyword The option using function fails because Python defines functions with def.
  • Starts with digit The option naming the function 2greet fails because identifiers cannot begin with a number.
  • Reserved word The option using class fails because Python keywords cannot be used as function names.
  • Wrong order The option placing greet before def does not follow Python’s function-definition syntax.

Question 19

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A program should print adult for any user age 18 or older, and print adult member only when that outer age test has already succeeded. The code is broken:

age = 16
member = True

if age >= 18:
    print("adult")
if member:
    print("adult member")

What is the best fix?

Options:

  • A. Make if member: the outer test and nest the age test

  • B. Replace the second if with elif member:

  • C. Indent if member: under if age >= 18:

  • D. Change the first test to if age >= 18 and member:

Best answer: C

Explanation: The fix is to nest the membership check inside the age check. In Python, an inner if runs only when the outer if is true, so adult member cannot print for someone under 18 while adult still prints for any adult.

This tests nested conditional flow and indentation. Two separate if statements are independent, so the second condition is checked whether or not the first one passed. In the broken code, member is tested even when age >= 18 is false, which lets adult member print for a minor.

To make the second test depend on the first, put it inside the first block:

  • Outer test: age >= 18
  • Inner test: member
  • Result: adult prints for every adult, and adult member prints only for adult members

Using elif is the opposite flow because it runs when the first test fails, and combining both conditions into one outer test removes the separate adult case.

  • Nesting the membership test under the age test matches the requirement that the inner check runs only after adulthood is confirmed.
  • Using elif member fails because elif is evaluated when age >= 18 is false.
  • Combining both checks in one outer condition is too strict and skips adult for adult non-members.
  • Making membership the outer test changes which condition controls the inner block.

Question 20

Topic: Block 4: Functions and Exceptions

What is printed by this code?

def label(first, second, third):
    print(first + "-" + second + "-" + third)

label("red", third="blue", second="green")
label("up", second="left", third="right")

Options:

  • A. First line: red-blue-green; second line: up-left-right

  • B. First line: red-green-blue; second line: up-right-left

  • C. First line: red-green-blue; second line: up-left-right

  • D. First line: red-blue-green; second line: up-right-left

Best answer: C

Explanation: Python assigns positional arguments first, then matches keyword arguments by name. That means the first call prints red-green-blue and the second prints up-left-right, even though the keyword arguments are written in a different order.

This tests how Python binds arguments in a valid mixed function call. A positional argument fills the next available parameter from left to right. After that, keyword arguments are matched to parameters by their names, not by the order they appear.

  • In label("red", third="blue", second="green"), first gets "red", second gets "green", and third gets "blue".
  • In label("up", second="left", third="right"), first gets "up", second gets "left", and third gets "right".

So the function prints red-green-blue on the first line and up-left-right on the second. The closest distractors come from treating keyword order as if it changed parameter order.

  • The option with red-blue-green treats third="blue" as if it filled the second slot, but keywords match by name.
  • The option with up-right-left makes the same mistake in the second call by swapping second and third.
  • The option swapping both lines assumes keyword arguments are processed by appearance order instead of parameter names.

Question 21

Topic: Block 1: Computer Programming and Python Fundamentals

A beginner wants this program to print nananana Batman!, but it raises an error:

part = "na"
line = part + 4 + " Batman!"
print(line)

Which replacement for the second line is the best fix?

Options:

  • A. line = part + part + " Batman!"

  • B. line = part * "4" + " Batman!"

  • C. line = part * 4 + " Batman!"

  • D. line = part + str(4) + " Batman!"

Best answer: C

Explanation: Strings use + to join strings and * with an integer to repeat a string. The fixed expression must repeat "na" four times and then append " Batman!".

Python treats string concatenation and string repetition as different operations. The + operator joins one string to another string, while the * operator repeats a string only when the other operand is an integer. In the broken code, part + 4 fails because Python cannot concatenate a str and an int directly.

  • part * 4 becomes nananana
  • nananana + " Batman!" becomes nananana Batman!

A common nearby mistake is turning 4 into the string "4"; that removes the type mismatch but still does not repeat the text.

  • Converting 4 to "4" avoids the original mismatch but produces na4 Batman!.
  • Multiplying by "4" is still invalid because string repetition needs an integer count.
  • Adding part only twice creates nana Batman!, which is too short.

Question 22

Topic: Block 4: Functions and Exceptions

A student wants this program to print True because "cat" is inside "concatenate", but it prints False. Without changing the function body, which replacement for the last line fixes the bug?

def has_fragment(text, fragment):
    return fragment in text

print(has_fragment("cat", "concatenate"))

Options:

  • A. print(has_fragment("concatenate", "cat"))

  • B. print(has_fragment(fragment="concatenate", text="cat"))

  • C. print(has_fragment("cat", fragment="concatenate"))

  • D. print(has_fragment(text="cat", fragment="concatenate"))

Best answer: A

Explanation: The function expects the larger text first and the smaller fragment second because it checks fragment in text. The original call reverses those positional arguments, so it tests whether "concatenate" is inside "cat", which is False.

This bug comes from how positional arguments are matched: Python assigns them by order. In has_fragment(text, fragment), the first argument becomes text and the second becomes fragment.

The function returns fragment in text, so the call must provide:

  • the full string as text
  • the smaller search string as fragment

With the original call, Python evaluates "concatenate" in "cat", which is False. Swapping the arguments makes the check "cat" in "concatenate", which is True.

Keyword arguments can also prevent order mistakes, but only if the correct values are assigned to the correct parameter names.

  • The keyword-based call with fragment="concatenate" and text="cat" is still reversed, even though the order on the line changes.
  • The mixed call keeps "cat" as the first argument, so text still gets the wrong value.
  • The all-keyword call is explicit, but it explicitly assigns the wrong values to the parameters.

Question 23

Topic: Block 4: Functions and Exceptions

A student is writing a loop that should keep asking for an age. If the entry is not a valid integer, the program should print Invalid age and ask again. It should stop only when the entered age is 0. Which change correctly preserves that control flow and catches the right exception?

while True:
    text = input("Age: ")
    try:
        age = int(text)
    # replace here
    if age == 0:
        break
    print(age)

Options:

  • A. Add except TypeError: then print Invalid age and continue.

  • B. Add except ValueError: then print Invalid age and continue.

  • C. Add except ValueError: then print Invalid age and break.

  • D. Add except IndexError: then print Invalid age and continue.

Best answer: B

Explanation: When int() cannot convert text like abc, Python raises ValueError. To keep the while loop asking again after bad input, the handler should catch ValueError and use continue, not break.

This item combines exception handling with loop control. In Python, int(text) raises ValueError when the string does not contain a valid integer. That means the handler should be specific to ValueError, not an unrelated exception such as TypeError or IndexError.

After printing the error message, continue skips the rest of the current loop iteration and goes back to the top of the while True loop for another prompt. Using break would end the loop immediately, which does not match the requirement to ask again after invalid input.

The key idea is to match the except clause to the actual failure that can happen, then choose loop control that fits the required behavior.

  • The option using TypeError fails because invalid numeric text in int() does not raise that exception.
  • The option using break stops the loop after bad input instead of prompting again.
  • The option using IndexError fails because no indexing operation is involved here.

Question 24

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

A student wants this exact one-line output:

C:\new\test

But this program prints the text on two lines and includes a tab:

print("C:\new\test")

What is the best fix?

Options:

  • A. Add end="" to the print() call.

  • B. Use triple quotes around the same text.

  • C. Use single quotes around the same text.

  • D. Replace each \ with \\ in the string.

Best answer: D

Explanation: Backslashes in Python strings begin escape sequences like \n for newline and \t for tab. Because C:\new\test contains both patterns, Python changes the text before printing. Escaping each backslash as \\ keeps the output on one line and shows the path exactly.

When Python reads a string literal, it interprets backslash escapes before print() runs. In C:\new\test, the \n becomes a newline and the \t becomes a tab, so the text is not displayed as a normal path.

print("C:\\new\\test")

Each \\ in the source code represents one literal backslash in the output. After escaping both backslashes, Python prints C:\new\test exactly as written. The key idea is that the problem is inside the string literal, not in the print() function itself.

  • Single quotes do not change how \n and \t are interpreted.
  • Triple quotes allow multiline literals, but backslash escapes still work inside them.
  • end="" only changes what print() adds after the string, not the characters already inside the string.

Question 25

Topic: Block 1: Computer Programming and Python Fundamentals

A beginner writes this code to test whether a calculation equals 0.3:

total = 0.1 + 0.2
if total == 0.3:
    print("match")
else:
    print("different")

The program should treat tiny floating-point representation errors as equal. Which TWO replacements for the if line are appropriate?

Options:

  • A. if str(total) == "0.3":

  • B. if abs(total - 0.3) < 1e-9:

  • C. if total == float("0.3"):

  • D. if round(total, 10) == round(0.3, 10):

  • E. if int(total) == int(0.3):

  • F. if round(total) == round(0.3):

Correct answers: B and D

Explanation: Floating-point numbers such as 0.1 and 0.2 are not stored exactly in binary, so direct == comparisons can fail for values that are mathematically equal. Using a small tolerance with abs() or rounding both sides to a known precision are common ways to compare them safely.

The core issue is floating-point representation. In Python, 0.1 + 0.2 may be stored as a value very close to 0.3, not exactly 0.3, so == can report False even though the math result is what you expect. A tolerance check like abs(total - 0.3) < 1e-9 asks whether the numbers are close enough, which is a standard fix. Rounding both values to the same number of decimal places can also work when the required precision is known in advance. By contrast, converting to int, comparing strings, or creating another float value such as float("0.3") does not solve the underlying precision problem. The key takeaway is to avoid exact equality for simple float results when tiny representation errors are acceptable.

  • Integer cast drops the fractional part, so many different decimal values can compare equal.
  • Exact float literal still performs direct float equality, which is the original problem.
  • Whole-number rounding reduces both values to 0, which is too imprecise for checking 0.3.
  • String comparison checks text formatting rather than numeric closeness.

Questions 26-30

Question 26

Topic: Block 2: Control Flow - Conditional Blocks and Loops

A student wants to print each word in uppercase. Indexes are not needed for this task, but the program raises an error:

words = ["code", "loop", "list"]

for word in range(len(words)):
    print(word.upper())

Which replacement for the for line is the best fix?

Options:

  • A. for word in range(words):

  • B. for word in len(words):

  • C. for word in range(len(words)):

  • D. for word in words:

Best answer: D

Explanation: When indexes are not needed, iterate directly over the sequence. Using for word in words: makes word hold each string from the list, so calling upper() works correctly.

The core idea is direct sequence iteration: use for item in sequence: when you need each element but not its position. In the broken code, range(len(words)) produces the integers 0, 1, and 2, so word becomes an integer instead of a string. Because integers do not have an upper() method, the loop fails.

Direct iteration fixes that immediately:

  • for word in words: assigns each list element to word
  • each element is a string
  • word.upper() then prints the uppercase version

Using indexes is unnecessary here and makes the loop harder to read for this task.

  • len(words) returns one integer, so it cannot be used directly as the iterable in a for loop.
  • range(words) is invalid because range() expects an integer argument, not a list.
  • Keeping range(len(words)) still makes word an integer, so word.upper() still fails.

Question 27

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

A beginner says this program takes the wrong branch. With word = "python", they expected pypyn because "th" is in the word, but the program prints pypyp.

word = "python"
piece = word[0:2] * 2

if "th" in piece:
    print(piece + word[-1])
else:
    print(piece + word[0])

What is the best fix?

Options:

  • A. Build piece with word[0:3] * 2.

  • B. Concatenate word[-1] + piece instead.

  • C. Build piece with word[2:4] * 2.

  • D. Check "th" in word, not in piece.

Best answer: D

Explanation: The condition is checking the wrong string. piece becomes "pypy", so "th" in piece is False, even though "th" is in word; changing the condition to use word produces the expected pypyn.

The key concept is that the membership operator in checks only the string on its right. Here, word[0:2] gives "py", and * 2 repeats it to make piece = "pypy". Because "th" in "pypy" is False, Python runs the else branch and prints piece + word[0], which is "pypyp".

  • word[0:2] gets the first two characters.
  • * 2 repeats that slice.
  • word[-1] would correctly give "n", but that branch never runs.
  • If the decision should depend on the original word, the condition must be if "th" in word:.

Changing the slice or the concatenation order changes the output text, but it does not fix the real cause of the wrong branch.

  • Changing piece to word[2:4] * 2 makes the repeated text "thth", so it no longer matches the expected "pypy" part.
  • Changing piece to word[0:3] * 2 produces "pytpyt" and still leaves the condition tied to the wrong string.
  • Reversing the concatenation order affects only the final output format; it does not fix the incorrect branch choice.

Question 28

Topic: Block 1: Computer Programming and Python Fundamentals

A beginner wants latest_points to store the updated total after 4 points are added, using the current value of points. The program prints 10 instead of 14.

points = 10
latest_points = points
points = points + 4
print(latest_points)

Which change is the best fix?

Options:

  • A. Replace latest_points = points with latest_points = 14.

  • B. Change points = points + 4 to points == points + 4.

  • C. Move latest_points = points below points = points + 4.

  • D. Change print(latest_points) to print(points).

Best answer: C

Explanation: Assignment binds a name to the value it has at that moment. latest_points gets 10 before points is reassigned, so it stays 10. Moving the assignment after the update makes latest_points receive 14.

In Python, a variable name does not automatically follow later changes to another variable. When latest_points = points runs, latest_points is assigned the current value of points, which is 10. Then points = points + 4 reassigns points to 14, but latest_points is still 10 until it is assigned again.

A correct fix is to update points first and then assign that updated value to latest_points:

points = 10
points = points + 4
latest_points = points
print(latest_points)

The key takeaway is that reassignment changes the value bound to one name; it does not automatically change other names assigned earlier.

  • Using == compares values and does not reassign points.
  • Printing points would show 14, but latest_points still would not store the updated total.
  • Hard-coding 14 ignores the actual variable value and does not demonstrate correct reassignment.

Question 29

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

A student is debugging this code. The summarize() function must receive a list, even when it gets only one value.

temps = [18, 21, 19, 23]
summarize(???)

Which expression should replace ??? so that summarize() receives a list containing only 21?

Options:

  • A. temps[2:3]

  • B. temps[:1]

  • C. temps[1:2]

  • D. temps[1]

Best answer: C

Explanation: Use temps[1:2] because slicing returns a new list, even when the slice has just one item. Simple indexing with temps[1] returns the element itself, not a one-element list.

In Python, list indexing and list slicing look similar but produce different kinds of results. Indexing with one position, such as temps[1], returns a single element, so the result is the integer 21. Slicing with a start and stop, such as temps[1:2], returns a new list containing the selected range.

Because the stop index is excluded, 1:2 includes only the item at index 1. That makes the result [21], which matches a function that expects a list argument. A slice can contain one element, many elements, or even be empty, but it is still a list. The closest mistakes here use valid slices with the wrong boundaries, so they return the wrong value.

  • temps[1] gets the correct value, but it returns 21, not a list.
  • temps[:1] is a slice, but it returns [18], which contains the first item.
  • temps[2:3] is also a one-item slice, but it returns [19], not [21].

Question 30

Topic: Block 3: Data Collections - Tuples, Dictionaries, Lists, and Strings

A student is debugging a function that should change the first character of any non-empty string to J. The code fails because it treats a string like a mutable list.

def fix_name(name):
    name[0] = "J"
    return name

print(fix_name("mark"))

Which replacement for the two indented lines inside fix_name makes the function work as intended for any non-empty string?

Options:

  • A. return "J" + name[1:]

  • B. return name[:1] + "J" + name[1:]

  • C. return name[1:] + "J"

  • D. return name.replace(name[0], "J")

Best answer: A

Explanation: In Python, strings are immutable, so name[0] = "J" is invalid. The fix is to return a new string made from the replacement character and the rest of the original string.

A Python string cannot be changed in place with indexed assignment. That works for lists, but with strings it raises a TypeError. When a function needs an updated string, it must create and return a new one instead.

Here, name[1:] returns all characters after the first one. Concatenating "J" in front gives the same string except for index 0.

def fix_name(name):
    return "J" + name[1:]

The key idea is to rebuild the string with slicing rather than trying to mutate it.

  • The option adding name[:1] keeps the original first character and inserts J after it.
  • The option appending J changes the last position, not the first one.
  • The option using replace(name[0], "J") can change every matching character, not just index 0.

Continue with full practice

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

Try PCEP-30-02 on Web View PCEP-30-02 Practice Test

Focused topic pages

Free review resource

Read the PCEP-30-02 Cheat Sheet on Tech Exam Lexicon for concept review before another timed run.

Revised on Thursday, May 14, 2026