Module 1 · Python · Drills

Drills: Advanced Python

Comprehensions, generators, decorators, and error handling only stick when your fingers know them. Type every drill before you reveal the solution — effortful recall is the whole point.

How to use this page Each drill is a small task. Attempt it first in the REPL or a file, run it, then click “Show solution” to compare. A different-but-correct answer is a win — that's fluency. Tick each box as you go; your progress is saved in this browser.

A · Warm-up reps Basic

Drill 1 comprehension

Rewrite this loop as a single list comprehension that keeps only the even squares:

out = []
for n in range(10):
    if n % 2 == 0:
        out.append(n * n)
Show solution
out = [n * n for n in range(10) if n % 2 == 0]
print(out)   # [0, 4, 16, 36, 64]

Read it as "expression · for · (optional) if". The filter goes at the end.

Drill 2 dict comp

Given names = ["Sam", "Mei", "Ali"], build a dict mapping each name to its length, e.g. {"Sam": 3, ...}.

Show solution
names = ["Sam", "Mei", "Ali"]
lengths = {name: len(name) for name in names}
print(lengths)   # {'Sam': 3, 'Mei': 3, 'Ali': 3}

Drill 3 generator

Write a generator chunks(items, size) that yields successive slices of a list. list(chunks([1,2,3,4,5], 2)) should give [[1,2],[3,4],[5]].

Show solution
def chunks(items, size):
    for i in range(0, len(items), size):
        yield items[i : i + size]

print(list(chunks([1, 2, 3, 4, 5], 2)))
# [[1, 2], [3, 4], [5]]

This is the exact shape you'll use to stream document chunks in the RAG module — lazy, memory-flat.

B · Stretch Intermediate

Drill 4 try/except

Write safe_int(s) that returns int(s), but returns 0 if the string isn't a valid number (catch ValueError). Test it on "42" and "oops".

Show solution
def safe_int(s):
    try:
        return int(s)
    except ValueError:
        return 0

print(safe_int("42"))    # 42
print(safe_int("oops"))  # 0

Catch the specific exception (ValueError), never a bare except:. This is EAFP — try it, handle the failure.

Drill 5 decorator

Write a decorator announce that prints "before" before the function runs and "after" after it, then returns the result. Apply it to a greet(name).

Show solution
def announce(fn):
    def wrapper(*args, **kwargs):
        print("before")
        result = fn(*args, **kwargs)
        print("after")
        return result
    return wrapper

@announce
def greet(name):
    return f"Hi {name}"

print(greet("Sam"))
# before / after / Hi Sam

Same wrapper shape as FastAPI's @app.get — wrap, add behaviour, return.

Drill 6 Counter

Given tags = ["ai","uae","ai","jobs","ai","uae"], use Counter to print the two most common tags with their counts.

Show solution
from collections import Counter

tags = ["ai", "uae", "ai", "jobs", "ai", "uae"]
for tag, n in Counter(tags).most_common(2):
    print(f"{tag}: {n}")
# ai: 3 / uae: 2

C · Build challenge Build

Mini-project Write log_levels.py: given a list of log lines, count how many of each level (INFO, WARN, ERROR) appeared and print them most-common first. This is the exact ops skim you'll do once DocChat is deployed.

Build · log-level counter

Input:

lines = [
    "INFO  server started",
    "WARN  slow query",
    "ERROR db lost",
    "INFO  request 200",
    "ERROR embedding timeout",
    "INFO  request 200",
]
Show solution
from collections import Counter

# comprehension: first word of each line is the level
levels = [line.split()[0] for line in lines]

for level, n in Counter(levels).most_common():
    print(f"{level}: {n}")
# INFO: 3 / ERROR: 2 / WARN: 1

The whole job is two ideas: a comprehension to extract the field, a Counter to tally. Extract-then-tally is half of all log and analytics code you'll ever write.

D · Rapid recall Flashcards

Click a card to flip it. Say the answer out loud before you flip — that's the rep that builds storage strength.

List comprehension syntax?
[expr for x in items if cond] — expression, for, optional filter.
click to flip
What does yield do?
Hands back one value and pauses, making the function a lazy generator.
click to flip
Purpose of a decorator?
Wraps a function to add behaviour without editing it — e.g. @app.get registers a route.
click to flip
What is Optional[str]?
A value that's a str or None. Modern form: str | None.
click to flip
Why with open(...)?
It auto-closes the file when the block ends — even if an error fires inside.
click to flip
What does Counter do?
Tallies items in a sequence. Counter(xs).most_common(n) gives the top n.
click to flip

E · Self-check before moving on

Tick each only if you can do it without looking:

Next All ticked? You now write Python the way Python wants to be written — and you've met every idea FastAPI is built on. Time to build real APIs: Module 2 — FastAPI Core.