TCX1002 | Python Midterm Cheatsheet (Weeks 1-4)

Comprehensive Python reference for NUS TCX1002 midterm: built-ins, data structures, numpy, regex, functools, itertools, patterns, traps

0. Exam Environment

ItemDetail
DateSat 14 Feb, 10:30am – 12:30pm
VenueLT19 (COM2 Level 1)
Format4 questions, 5 marks each, 2 hours
EnvironmentSEB (no internet, no syntax help, can run code)
Test casesPublic visible, private hidden
HardcopyAllowed (unlimited)
Seat71

Allowed Modules

1import datetime
2import functools    # reduce, partial, lru_cache
3import itertools    # combinations, permutations, chain, product
4import math         # ceil, floor, sqrt, log, gcd
5import numpy as np  # full numpy
6import random       # randint, choice, shuffle
7import re           # findall, match, search, sub

from collections import ... is NOT ALLOWED! No defaultdict, Counter, deque, namedtuple.

defaultdict Replacements

 1# ❌ BANNED: defaultdict(int)
 2counts[key] += 1  # KeyError if key doesn't exist
 3
 4# ✅ Regular dict counting
 5counts[key] = counts.get(key, 0) + 1
 6
 7# ❌ BANNED: defaultdict(list)
 8groups[key].append(val)
 9
10# ✅ setdefault
11groups.setdefault(key, []).append(val)
12
13# ❌ BANNED: defaultdict(set)
14groups[key].add(val)
15
16# ✅ setdefault with set
17groups.setdefault(key, set()).add(val)

1. Built-in Functions

FunctionWhat It DoesExample
len(x)Lengthlen([1,2,3]) → 3
sum(x)Sumsum([1,2,3]) → 6
min(x) / max(x)Min/maxmax([3,1,2]) → 3
abs(x)Absolute valueabs(-5) → 5
round(x, n)Round to n decimalsround(3.14, 1) → 3.1
range(a,b,s)Integer sequencelist(range(0,10,2)) → [0,2,4,6,8]
enumerate(x, start)Index + valuelist(enumerate('ab',1)) → [(1,‘a’),(2,‘b’)]
zip(a, b)Pair up elementslist(zip([1,2],['a','b'])) → [(1,‘a’),(2,‘b’)]
sorted(x, key=, reverse=)New sorted listsorted([3,1,2]) → [1,2,3]
any(x) / all(x)Any/all truthyany([0,1,0]) → True
isinstance(x, type)Type checkisinstance(5, int) → True
map(fn, iter)Apply fn to eachlist(map(str, [1,2])) → [‘1’,‘2’]
filter(fn, iter)Keep if fn truelist(filter(lambda x:x>0, [-1,2])) → [2]

TRAP: round() uses banker’s rounding (nearest even). round(2.5) → 2! Use math.ceil() for “always round up”.

TRAP: map(fn, iter) has NO key= parameter. That’s sorted(). Don’t mix them up.


2. String Methods

MethodWhatExample
s.lower() / s.upper()Case conversion"Hi".lower()"hi"
s.strip()Remove whitespace" hi ".strip()"hi"
s.split(sep)Split to list"a,b".split(",")['a','b']
sep.join(lst)Join list to string",".join(['a','b'])"a,b"
s.replace(old, new)Replace substring"hello".replace("l","r")"herro"
s.find(sub)Index or -1"hello".find("ll") → 2
s.startswith(p) / s.endswith(p)Prefix/suffix check"hello".startswith("he") → True
s.isdigit() / s.isalpha()All digits/letters?"123".isdigit() → True
s.count(sub)Count occurrences"hello".count("l") → 2
ord(c) / chr(n)Char ↔ ASCIIord('A') → 65, chr(65) → ‘A’
f"{x:.2f}"Format floatf"{3.14159:.2f}""3.14"

String is IMMUTABLE — methods return new strings, never modify in place.

Empty string edge case: "".split(" ")[""] (NOT []!)


3. List Methods

MethodWhatIn-Place?Returns
lst.append(x)Add to endYesNone
lst.extend(iter)Add all itemsYesNone
lst.insert(i, x)Insert at indexYesNone
lst.pop(i)Remove & return at iYesThe item
lst.remove(x)Remove first xYesNone
lst.sort(key=, reverse=)Sort in placeYesNone
lst.reverse()Reverse in placeYesNone
lst.index(x)Find index of xNoint
lst.count(x)Count occurrencesNoint
lst.copy() or lst[:]Shallow copyNoNew list

sorted() vs .sort(): “sortED = nEw Data” — sorted() returns new list, .sort() modifies in place and returns None.


4. Dictionary Methods

MethodWhatExample
d[key]Get value (KeyError if missing)d['name']
d.get(key, default)Get or default (no error)d.get('age', 0)
d.setdefault(key, default)Get or set+return defaultd.setdefault('age', 0)
d.keys() / d.values()All keys / valueslist(d.keys())
d.items()(key, value) pairsfor k, v in d.items()
d.update(other)Merge other dictd.update({'a': 1})
d.pop(key)Remove & return valued.pop('name')
key in dKey exists?'name' in d

Dict Counting Pattern (No collections!)

1counts = {}
2for item in items:
3    counts[item] = counts.get(item, 0) + 1

Dict Grouping Pattern (No defaultdict!)

1groups = {}
2for item in items:
3    key = item[0]  # or whatever grouping logic
4    groups.setdefault(key, []).append(item)

5. Set Operations

OperationOperatorMethodResult
Intersectiona & ba.intersection(b)In BOTH
Uniona | ba.union(b)In EITHER
Differencea - ba.difference(b)In a, NOT in b
Symmetric Diffa ^ ba.symmetric_difference(b)In ONE, not both
Subseta <= ba.issubset(b)All of a in b?
Superseta >= ba.issuperset(b)All of b in a?
1a = {1, 2, 3, 4}
2b = {3, 4, 5, 6}
3a & b   # {3, 4}             — common
4a | b   # {1, 2, 3, 4, 5, 6} — all
5a - b   # {1, 2}             — only in a
6a ^ b   # {1, 2, 5, 6}       — not shared

Set from list: set([1,2,2,3]){1, 2, 3} (deduplicates)

frozenset: Immutable set, can be dict key. frozenset({1, 2})


6. Lambda + Sorting Patterns

1# Lambda = anonymous function
2lambda x: x[1]           # get index 1
3lambda x: -x[1]          # sort descending
4lambda x: (x[0], -x[1])  # multi-key: first asc, second desc
5
6# ⚠️ sorted uses key=, map does NOT
7sorted(data, key=lambda x: -x[1])    # ✅
8map(lambda x: x**2, data)            # ✅ NO key=

Dense Ranking (1-2-2-3)

1sorted_rows = sorted(rows, key=lambda x: (-x[1], -x[2], x[0]))
2rank, prev = 1, None
3result = []
4for row in sorted_rows:
5    curr = (row[1], row[2])
6    if prev is not None and prev != curr:
7        rank += 1
8    result.append((rank, *row))
9    prev = curr

7. Iteration Patterns

enumerate — index + value

1for i, val in enumerate(lst):          # i=0,1,2...
2for i, val in enumerate(lst, start=1): # i=1,2,3...

zip — pair up lists

1for a, b in zip(list1, list2):    # stops at SHORTER list
2    print(a, b)
3
4# Unzip:
5pairs = [(1,'a'), (2,'b')]
6nums, letters = zip(*pairs)  # (1,2), ('a','b')
7
8# Element-wise sum:
9[x + y for x, y in zip(a, b)]

Comprehensions

 1# List
 2[x**2 for x in range(5)]                    # [0, 1, 4, 9, 16]
 3[x for x in data if x > 0]                  # filter
 4[x**2 for x in data if x % 2 == 0]          # filter + transform
 5
 6# Dict
 7{k: v for k, v in pairs}
 8{word: len(word) for word in words}
 9
10# Set
11{x for x in data if x > 0}
12
13# Nested (2D matrix)
14[[i*3+j for j in range(3)] for i in range(3)]

Functional (map/filter — no loops)

1filter(lambda x: abs(x) > threshold, changes)  # keep matching
2map(lambda x: x**2, values)                     # transform each
3sum(map(lambda x: x**2, filter(lambda x: x > 0, data)))  # chain
4
5# ⚠️ map() and filter() return iterators, wrap in list() to see contents
6list(map(lambda x: x*2, [1,2,3]))  # [2, 4, 6]

8. functools Module

1from functools import reduce

reduce — fold list to single value

 1from functools import reduce
 2
 3# Sum (same as sum())
 4reduce(lambda acc, x: acc + x, [1, 2, 3, 4])  # 10
 5
 6# Product
 7reduce(lambda acc, x: acc * x, [1, 2, 3, 4])  # 24
 8
 9# Max (same as max())
10reduce(lambda acc, x: acc if acc > x else x, [3, 1, 4, 1, 5])  # 5
11
12# With initial value
13reduce(lambda acc, x: acc + x, [1, 2, 3], 100)  # 106

How reduce works: reduce(fn, [a, b, c, d]) = fn(fn(fn(a, b), c), d) — left fold.

Other functools

 1from functools import partial, lru_cache
 2
 3# partial — pre-fill some arguments
 4add10 = partial(lambda x, y: x + y, 10)
 5add10(5)  # 15
 6
 7# lru_cache — memoization (cache results)
 8@lru_cache(maxsize=None)
 9def fib(n):
10    if n < 2: return n
11    return fib(n-1) + fib(n-2)

9. itertools Module

1import itertools

Most Useful Functions

 1from itertools import combinations, permutations, chain, product, accumulate
 2
 3# combinations — choose k from n (order doesn't matter)
 4list(combinations([1,2,3], 2))
 5# [(1,2), (1,3), (2,3)]
 6
 7# permutations — arrange k from n (order matters)
 8list(permutations([1,2,3], 2))
 9# [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]
10
11# chain — flatten multiple iterables into one
12list(chain([1,2], [3,4], [5]))
13# [1, 2, 3, 4, 5]
14
15# product — cartesian product (all combinations)
16list(product([1,2], ['a','b']))
17# [(1,'a'), (1,'b'), (2,'a'), (2,'b')]
18
19# accumulate — running totals (like prefix sum)
20list(accumulate([1, 2, 3, 4]))
21# [1, 3, 6, 10]
22
23# accumulate with custom function
24list(accumulate([3, 1, 4, 1, 5], max))
25# [3, 3, 4, 4, 5]  — running max!

Quick Reference Table

FunctionWhatOrdered?Example
combinations(iter, k)Choose k itemsNo (sorted)C(n,k) combos
permutations(iter, k)Arrange k itemsYesP(n,k) arrangements
product(A, B)All pairsYesCartesian product
chain(A, B)Concat iterablesN/AFlatten
accumulate(iter, fn)Running foldN/APrefix sum/max

10. Common Coding Patterns

Two-Pointer Merge

 1def merge(l1, l2, comp=lambda x, y: x - y):
 2    i, j, out = 0, 0, []
 3    while i < len(l1) and j < len(l2):
 4        if comp(l1[i], l2[j]) <= 0:
 5            out.append(l1[i]); i += 1
 6        else:
 7            out.append(l2[j]); j += 1
 8    out.extend(l1[i:])
 9    out.extend(l2[j:])
10    return out

Sliding Window

1L, R = 0, window - 1
2while R < len(data):
3    window_slice = data[L:R+1]
4    # process window
5    L += 1; R += 1

Track-Seen (Find Duplicates)

 1seen = set()
 2added = set()
 3duplicates = []
 4for x in lst:
 5    if x in seen:
 6        if x not in added:
 7            duplicates.append(x)
 8            added.add(x)
 9    else:
10        seen.add(x)

Dict Counting (Word Frequency)

1counts = {}
2for word in text.lower().split():
3    counts[word] = counts.get(word, 0) + 1

Group by Key (No defaultdict!)

1groups = {}
2for i, (x, y) in enumerate(points):
3    groups.setdefault(('row', y), []).append((i, x))

11. NumPy Quick Reference

1import numpy as np

Array Creation

1np.array([1, 2, 3])              # from list
2np.zeros((3, 4))                 # 3x4 of zeros
3np.ones((2, 3))                  # 2x3 of ones
4np.arange(0, 10, 2)              # [0, 2, 4, 6, 8]
5np.linspace(0, 1, 5)             # [0, 0.25, 0.5, 0.75, 1.0]
6data.reshape(-1, 4)              # reshape, -1 = auto

Indexing & Slicing

1a[1:-1]            # exclude first and last
2a[:, np.newaxis]   # add dimension (for broadcasting)
3a[np.newaxis, :]   # add dimension at front
4a[a > 5]           # boolean indexing (filter)

Broadcasting Pattern (Pairwise Operations)

1# Make (n,1,d) and (1,n,d) for pairwise comparison
2R = a[:, np.newaxis, :]   # shape (n, 1, d)
3C = a[np.newaxis, :, :]   # shape (1, n, d)
4diff = R - C               # shape (n, n, d) pairwise differences

Common Operations

 1np.abs(x)                  # element-wise absolute value
 2np.square(x)               # element-wise square
 3np.sqrt(x)                 # element-wise square root
 4np.sum(x, axis=0)          # sum along axis (0=rows, 1=cols, 2=depth)
 5np.mean(x, axis=0)         # mean along axis
 6np.dot(a, b.T)             # matrix multiplication
 7np.linalg.norm(a, axis=1)  # vector norms (per row)
 8np.outer(a, b)             # outer product
 9np.where(condition)        # indices where True
10np.argsort(a)              # indices that would sort the array
11np.argmax(a)               # index of max value

“For Loop → NumPy” Conversion Patterns

 1# ❌ For loop (slow)
 2result = []
 3for x in data:
 4    if x > threshold:
 5        result.append(x ** 2)
 6
 7# ✅ NumPy (fast, vectorized)
 8a = np.array(data)
 9mask = a > threshold
10result = a[mask] ** 2
11
12# ❌ For loop (pairwise distances)
13for i in range(n):
14    for j in range(n):
15        dist[i][j] = sum(abs(a[i] - a[j]))
16
17# ✅ NumPy (broadcasting)
18diff = a[:, np.newaxis, :] - a[np.newaxis, :, :]
19dist = np.sum(np.abs(diff), axis=2)

Distance Matrices

 1# Setup: a is (n, d) array
 2R = a[:, np.newaxis, :]
 3C = a[np.newaxis, :, :]
 4D = R - C
 5
 6# Manhattan: sum of |differences|
 7manhattan = np.sum(np.abs(D), axis=2)
 8
 9# Euclidean: sqrt(sum of squared differences)
10euclidean = np.sqrt(np.sum(np.square(D), axis=2))
11
12# Cosine: 1 - (dot / product of norms)
13dot_products = np.dot(a, a.T)
14norms = np.linalg.norm(a, axis=1)
15cosine_dist = 1 - dot_products / np.outer(norms, norms)

Local Peaks (Vectorized)

1x = np.array(values)
2lefts  = x[:-2]      # all except last 2
3currs  = x[1:-1]     # middle elements
4rights = x[2:]       # all except first 2
5scale = 1 + threshold
6peaks = np.where((currs > lefts * scale) & (currs > rights * scale))[0] + 1

12. Regex Quick Reference

1import re

Core Functions

1re.findall(pattern, text)    # all matches as list of strings
2re.match(pattern, text)      # match at START only → match object or None
3re.search(pattern, text)     # first match ANYWHERE → match object or None
4re.sub(pattern, repl, text)  # replace all matches
5
6match.group(0)               # full match
7match.group(1)               # first capture group (first parentheses)

Pattern Syntax

PatternMeaningExample
.Any character (except newline)a.c matches “abc”
[A-Z]One uppercase letter[A-Z]{3} = 3 uppercase
[^A-Z]NOT uppercaseNegated set
[0-9] or \dOne digit\d{4} = 4 digits
\wWord char (letter/digit/_)\w+ = one or more word chars
\sWhitespace\s+ = one or more spaces
{2,3}2 to 3 repetitions[A-Z]{2,3} = 2 or 3 uppercase
+1 or more\d+ = one or more digits
*0 or more\d* = zero or more digits
?0 or 1 (optional)colou?r matches “color” and “colour”
^ / $Start / end of string^Hello = starts with Hello
(...)Capture group(\d+) captures digits
(?:...)Non-capturing groupGroup without capturing
(?=...)LookaheadAssert what follows
(?!...)Negative lookaheadAssert what does NOT follow
(?<=...)LookbehindAssert what precedes
(?<!...)Negative lookbehindAssert what does NOT precede

Common Patterns

 1# Extract all numbers
 2re.findall(r'\d+', "abc 123 def 456")  # ['123', '456']
 3
 4# Validate email-like pattern
 5re.match(r'^[\w.]+@[\w.]+\.\w+$', "user@mail.com")
 6
 7# Split on multiple delimiters
 8re.split(r'[,;\s]+', "a, b; c d")  # ['a', 'b', 'c', 'd']
 9
10# Replace with function
11re.sub(r'\d+', lambda m: str(int(m.group()) * 2), "a1b2c3")  # "a2b4c6"
12
13# Overlapping matches (lookahead trick)
14re.findall(r'(?=([A-Z]{2}\d+))', text)

13. Python Traps (Common Exam Tricks)

Mutable Default Arguments

 1# ❌ TRAP: same list reused across calls
 2def add(item, lst=[]):
 3    lst.append(item)
 4    return lst
 5add(1)  # [1]
 6add(2)  # [1, 2]  ← NOT [2]!
 7
 8# ✅ FIX:
 9def add(item, lst=None):
10    if lst is None:
11        lst = []
12    lst.append(item)
13    return lst

List Aliasing

1a = [1, 2, 3]
2b = a          # SAME object! b IS a
3b.append(4)
4print(a)       # [1, 2, 3, 4] ← a changed too!
5
6# FIX: b = a[:] or b = a.copy() or b = list(a)

Negative Indexing

1lst = [10, 20, 30]
2lst[-1]    # 30 (last element) — NOT an error!
3# Always check index BEFORE accessing:
4if pointer == -1:
5    return None
6val = lst[pointer]

Scope

1x = 10
2def foo():
3    x = 5       # local x, global unchanged
4foo()
5print(x)        # 10
6
7# To modify global: use `global x` inside function

is vs ==

1a = [1, 2]
2b = [1, 2]
3a == b    # True  (same content)
4a is b    # False (different objects)

List Multiplication with Mutables

1# ❌ TRAP:
2matrix = [[]] * 3      # 3 refs to SAME list
3matrix[0].append(1)    # ALL change: [[1],[1],[1]]
4
5# ✅ FIX:
6matrix = [[] for _ in range(3)]  # 3 SEPARATE lists

Loop Variable Scope Leaking

1for i in range(5):
2    pass
3print(i)  # 4 ← i survives after the loop! (NOT block-scoped)

Mutation During Iteration

1# ❌ TRAP: removing while iterating
2lst = [1, 2, 3, 4, 5]
3for x in lst:
4    if x % 2 == 0:
5        lst.remove(x)  # Skips elements!
6
7# ✅ FIX: iterate over copy, or use comprehension
8lst = [x for x in lst if x % 2 != 0]

14. math Module

1import math
FunctionWhatExample
math.ceil(x)Round UPmath.ceil(2.1) → 3
math.floor(x)Round DOWNmath.floor(2.9) → 2
math.sqrt(x)Square rootmath.sqrt(16) → 4.0
math.log(x)Natural logmath.log(math.e) → 1.0
math.log10(x)Log base 10math.log10(100) → 2.0
math.gcd(a, b)Greatest common divisormath.gcd(12, 8) → 4
math.factorial(n)n!math.factorial(5) → 120
math.pi3.14159…
math.e2.71828…
math.infInfinityGood for initial min/max

No import alternative: x ** 0.5 = math.sqrt(x), float('inf') = math.inf


15. Exam Strategy

4 questions, 30 min each:

  1. Read ALL questions first (5 min) — rank by difficulty, easiest first
  2. Per question (~25 min):
    • Read spec carefully (3 min) — underline edge cases, constraints
    • Plan approach (2 min) — what data structure, what pattern
    • Implement (15 min) — happy path first
    • Test & edge cases (5 min) — run public tests, then self-test edges
  3. Reserve last 10 min — re-read specs, check hidden edge cases

Edge Cases Checklist (Self-Test!)

Since private test cases are hidden, always self-test:

  • Empty input ([], "", {})
  • Single element
  • All same / all unique
  • Negative numbers / zero
  • Already sorted / reverse sorted
  • Boundary (first, last element)
  • Very large input (will your O(n²) TLE?)

If Stuck

  • Move to next question, come back with fresh eyes
  • Write pseudocode comments even if you can’t code it (logic credit)
  • print() debug to understand what’s happening

Source

Session: 2026-02-12 session notes Practice files: 000_mods/TCX1002/midterm_prep/p01-p12 Tutorial reviews: T2 REVIEW.md Date learned: 2026-02-12 Context: Midterm prep — coverage matrix analysis, gap drilling, exam environment discovery


Last updated: 2026-02-12

comments powered by Disqus