Code Tracing and Python Expressions, CS303E – Study Notes
offline

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.


TL;DR

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.


Key Patterns and Tracing Tips

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.


Core Content

Code Tracing: Worked Examples

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 Evaluation Reference

Expression

Result

Why

max({"A":3, "Z":9, "W":-5}.keys())

'Z'

Max of dict keys by string comparison

"texas longhorns"[-10::2]

'slnhrs'

Start at index -10 ('s'), step 2

"a!!b!cde!".split("!")

['a', '', 'b', 'cde', '']

Splits on each !, keeping empties

" \nxyz abc ".lstrip()

'xyz abc '

Strips leading whitespace only

"aababcabcd".rindex("b")

7

Rightmost index of 'b'

{1,2,5} ^ {2, 4}

{1, 4, 5}

Symmetric difference

"wow" >= "wow wow"

False

"wow" is a prefix but shorter

[2, 5, 1].append(7)

None

append modifies in place

{"A":3, "Z":9, "W":-5}["Z"]

9

Dict access by key

[(x*x - 1) for x in range(5)]

[-1, 0, 3, 8, 15]

List comprehension: 0-1, 1-1, 4-1, 9-1, 16-1

Writing Python Expressions

These must be single-line expressions with no assignments, loops, or if statements.

Task

Expression

Squares of 1 through 100

[x**2 for x in range(1, 101)]

All integers in L are unique

len(L) == len(set(L))

Set of lowercase vowels in string s

{c for c in s if c in 'aeiou'}

First half of even-length string s

s[:len(s)//2]

Count of even integers in list L

len([x for x in L if x % 2 == 0])

Average of list of floats L

sum(L) / len(L)

String s repeated 100 times

s * 100

Tuple of integers below and above float num

(int(num), int(num) + 1)

Pi to 5 decimal places, right-justified in width 10

f'{math.pi:>10.5f}'

Number of unique values in dict d

len(set(d.values()))

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.


Common Misconceptions

  • 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.


Why It Matters / Exam Flags

  • ⚠️ 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.


Quick Self-Test

  1. What does "hello world".split() return? → ['hello', 'world']

  1. What does "hello world".split(" ") return? → ['hello', 'world'] (same here, but they differ on consecutive spaces)

  1. What does [3, 1, 2].sort() evaluate to? → None

  1. True or False: for i in range(5) makes i take values 0, 1, 2, 3, 4. → True

  1. What type is {"a": 1}["a"]? → int (the value 1)


Practice Q&A

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.


Connections to Other Topics

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.


Related Terms / Search Tags

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