Difficulty: Intermediate | Prerequisites: All material from Parts 1 and 2 (data types, classes, recursion, file I/O).
Code tracing and expression evaluation make up a large portion of the CS303E Exam 3. These questions test whether you can mentally execute Python code and predict its output, or write a single expression to produce a given result. The tracing questions cover loops over strings and lists, set toggling, recursive dictionary building, class behaviour, and list mutation through function calls. The expression questions test list comprehensions, string formatting, slicing, and built-in functions. If you can do these confidently by hand, you are well prepared.
About a third of the exam asks you to read Python code and write its output. Another chunk asks you to write single-line Python expressions. The key is careful, step-by-step execution: watch for range(len(s)) producing integers (not characters), in-place list mutation affecting the caller, and append() / sort() returning None.
range(len(s)) produces integers, not characters
When you write for ch in range(len(s)), the variable ch takes integer values 0, 1, 2, etc. It is never equal to "a" or any other character. If the code then tests if ch == "a", that condition is always False. This is a deliberate trap.
In-place methods return None
list.append(), list.sort(), set.add(), set.remove() all modify the object and return None. If you see an expression like [2, 5, 1].append(7), the result is None, not [2, 5, 1, 7].
Set toggling pattern
A loop that adds an element to a set if absent, and removes it if present, effectively keeps only elements that appear an odd number of times.
Watch for logic errors vs runtime errors vs syntax errors
A logic error means the code runs without crashing but produces the wrong answer. The sumAdjacentProducts example uses L[i - 1] * L[i] starting at i = 0, which wraps around to L[-1] (the last element), adding an extra unwanted product. No crash, just a wrong result.
end=" " in print()
When you see print(x, end=" "), the output ends with a space instead of a newline. Multiple prints on one line, separated by spaces.
Q12: range(len(s)) trap
s = "what a beautiful day"
a_count = 0
for ch in range(len(s)):
if ch == "a":
a_count += 1
print(a_count)Output: 0
range(len(s)) produces integers 0 through 19. The variable ch is never the string "a", so the condition is always False.
Q13: Set add/remove toggle
nums = [2, 2, 1, 4, 1, 4, 3, 4, 2, 2]
s = set()
for x in nums:
if x not in s:
s.add(x)
else:
s.remove(x)
print(s)Trace: 2 in, 2 out, 1 in, 4 in, 1 out, 4 out, 3 in, 4 in, 2 in, 2 out. Final set: {3, 4}.
The pattern keeps elements that appear an odd number of times.
Q14: String sorting by ASCII
words = ["banana", "BANANA", "ban", "bandana", "Alabama"]
words.sort()
print(words)Output: ['Alabama', 'BANANA', 'ban', 'banana', 'bandana']
Uppercase letters have lower ASCII values than lowercase, so 'A' (65) < 'B' (66) < 'a' (97) < 'b' (98). All uppercase-starting words come first.
Q15: Insertion-like shift
def f(lst, j):
val = lst[j]
i = j + 1
while (i < len(lst)) and (lst[i] < val):
lst[i - 1] = lst[i]
i += 1
lst[i - 1] = val
return lst
print(f([7, 2, 14, 6, 9, 13, 15, 19], 2))Output: [7, 2, 6, 9, 13, 14, 15, 19]
val = 14. Elements at indices 3, 4, 5 (values 6, 9, 13) are all less than 14, so they shift one position left. Then 14 is placed at index 5.
Q16: Recursive character counting
def f(s):
if s == "":
return {}
res = f(s[1:])
if s[0] not in res:
res[s[0]] = 0
res[s[0]] += 1
return res
print(f("an assassin sins"))Output: {'n': 3, 's': 5, 'i': 1, ' ': 3, 'a': 3}
The function recursively builds a frequency dictionary. Count each character in "an assassin sins": a appears 3 times, n appears 3 times, space appears 3 times, s appears 5 times, i appears 1 time. (Any ordering of the dictionary is acceptable.)
Q17: Object comparison
p1 = Pokemon(7, "Squirtle")
p2 = Pokemon(7, "Squirtle")
print(p1 == p2, end=" ")
print(p1.number == p2.number, end=" ")
print(str(p1) == str(p2), end=" ")Output: False True True
p1 == p2 is False because they are different objects (no __eq__ defined). Their .number attributes are both 7, so that comparison is True. Their __str__ outputs are identical strings, so str(p1) == str(p2) is True.
Q18: List mutation through function calls
def f(lst):
if lst[-1] > 5:
lst.append(8)
print(lst, end=" ")
nums = [1, 9]
f(nums)
print(nums, end=" ")
f(nums)Output: [1, 9, 8] [1, 9, 8] [1, 9, 8, 8]
First call: lst[-1] is 9 > 5, so 8 is appended. nums is now [1, 9, 8]. Second call: lst[-1] is 8 > 5, so another 8 is appended. nums is now [1, 9, 8, 8].
Expression | Result | Why |
|---|---|---|
|
| Max of dict keys by string comparison |
|
| Start at index -10 ( |
|
| Splits on each |
|
| Strips leading whitespace only |
|
| Rightmost index of |
|
| Symmetric difference |
|
|
|
|
|
|
|
| Dict access by key |
|
| List comprehension: 0-1, 1-1, 4-1, 9-1, 16-1 |
These must be single-line expressions with no assignments, loops, or if statements.
Task | Expression |
|---|---|
Squares of 1 through 100 |
|
All integers in |
|
Set of lowercase vowels in string |
|
First half of even-length string |
|
Count of even integers in list |
|
Average of list of floats |
|
String |
|
Tuple of integers below and above float |
|
Pi to 5 decimal places, right-justified in width 10 |
|
Number of unique values in dict |
|
List comprehension pattern
The general form is [expression for variable in iterable if condition]. The if part is optional. This replaces loops for building lists, and {...} with the same syntax builds a set.
String formatting with f-strings
f'{value:>10.5f}' means: format value as a float with 5 decimal places, right-justified in a field of width 10. The > means right-align, 10 is the total width, .5f is 5 decimal places.
Students often confuse for ch in range(len(s)) with for ch in s. The first gives integers; the second gives characters.
Students often forget that append() returns None and write things like new_list = old_list.append(x), leaving new_list as None.
Students often think a logic error will cause a crash. It will not; the program runs fine but gives a wrong answer.
Students often miss that "wow" >= "wow wow" is False. A shorter string that is a prefix of a longer one is always "less than" the longer one.
Students often write int(num) and int(num) + 1 to get the integers around a float, but forget to wrap them in a tuple with parentheses and a comma.
⚠️ The range(len(s)) trap appears frequently. Read carefully whether the loop variable is an index or a character.
⚠️ Know the output type of every expression: 9 is an int, 9.0 is a float, '9' is a str, None is NoneType. The exam deducts marks for wrong types.
⚠️ Be able to trace set toggling (add if absent, remove if present) by hand.
⚠️ List comprehensions replace loops for single-expression answers. If you think you need a loop, try a comprehension.
⚠️ String formatting: f'{math.pi:>10.5f}' is the pattern. Know the >, <, width, and .Nf components.
⚠️ The sumAdjacentProducts bug (L[i-1] at i=0 wraps to the last element) is a logic error, not a runtime error.
What does "hello world".split() return? → ['hello', 'world']
What does "hello world".split(" ") return? → ['hello', 'world'] (same here, but they differ on consecutive spaces)
What does [3, 1, 2].sort() evaluate to? → None
True or False: for i in range(5) makes i take values 0, 1, 2, 3, 4. → True
What type is {"a": 1}["a"]? → int (the value 1)
Q: What does the following code print?
s = "what a beautiful day"
a_count = 0
for ch in range(len(s)):
if ch == "a":
a_count += 1
print(a_count)A: 0. The loop variable ch is an integer, never equal to the string "a".
Q: Write a single Python expression that returns the number of even integers in a list L.
A: len([x for x in L if x % 2 == 0])
Q: What does {1,2,5} ^ {2, 4} evaluate to?
A: {1, 4, 5}. Symmetric difference keeps elements in one set but not both.
Q: What does " \nxyz abc ".lstrip() evaluate to?
A: 'xyz abc '. lstrip() removes leading whitespace (including \n), leaving trailing whitespace intact.
Q: What is the output of the sumAdjacentProducts code (Q9 from the exam)? What type of error does it contain?
A: Logic error. The code runs without crashing, but when i = 0, L[i - 1] accesses L[-1] (the last element), adding an extra product that was not intended.
Code tracing ties together everything from the course. You need to understand data types and mutability to predict what happens when a list is passed to a function, string methods to trace split() and lstrip() calls, set operations to trace symmetric difference and toggling patterns, classes to predict == behaviour, and recursion to follow recursive dictionary and string building. The expression-writing questions draw heavily on list comprehensions, which are the single-expression replacement for loops, and on string formatting, which is tested in the specific form of right-justified f-strings.
Tags: Python code tracing, expression evaluation, list comprehension, set comprehension, string formatting, f-string, range, len, split, lstrip, rindex, rstrip, append, sort, set operations, symmetric difference, logic error, runtime error, syntax error, object comparison, list mutation, pass by reference, in-place methods, None, CS303E, UT Austin, Dr. Bill Young