Filtering and Transforming
Add Derived Field
Enrich a list of records by computing a new field from existing ones — here,
total = price × qty. The trace shows each record r alongside the
computed total, one iteration at a time.
By hand
Loop over the records. Compute total from the current record's fields, then
spread the original record plus the new field into a fresh dict with {**r, ...}.
naive.py
Replay: real traced execution (multi-file project)
records = [
{'n': 'a', 'p': 2, 'q': 5},
{'n': 'b', 'p': 5, 'q': 6},
{'n': 'c', 'p': 3, 'q': 7},
{'n': 'd', 'p': 8, 'q': 3},
{'n': 'e', 'p': 4, 'q': 8},
{'n': 'f', 'p': 6, 'q': 9},
]
# trace: ignore records, enriched
enriched = []
for r in records:
total = r['p'] * r['q']
new = {**r, 'total': total}
enriched.append(new)
print('RESULT:', [d['total'] for d in enriched])
{'n': 'a', 'p': 2, 'q': 5},
1records = [2 {'n': 'a', 'p': 2, 'q': 5},3 {'n': 'b', 'p': 5, 'q': 6},{'n': 'b', 'p': 5, 'q': 6},
2{'n': 'a', 'p': 2, 'q': 5},3{'n': 'b', 'p': 5, 'q': 6},4{'n': 'c', 'p': 3, 'q': 7},{'n': 'c', 'p': 3, 'q': 7},
3{'n': 'b', 'p': 5, 'q': 6},4{'n': 'c', 'p': 3, 'q': 7},5{'n': 'd', 'p': 8, 'q': 3},{'n': 'd', 'p': 8, 'q': 3},
4{'n': 'c', 'p': 3, 'q': 7},5{'n': 'd', 'p': 8, 'q': 3},6{'n': 'e', 'p': 4, 'q': 8},{'n': 'e', 'p': 4, 'q': 8},
5{'n': 'd', 'p': 8, 'q': 3},6{'n': 'e', 'p': 4, 'q': 8},7{'n': 'f', 'p': 6, 'q': 9},{'n': 'f', 'p': 6, 'q': 9},
6 {'n': 'e', 'p': 4, 'q': 8},7 {'n': 'f', 'p': 6, 'q': 9},8]records = [
1records = [2 {'n': 'a', 'p': 2, 'q': 5},enriched = []
9# trace: ignore records, enriched10enriched = []11for r in records:r ← {'n': 'a', 'p': 2, 'q': 5}, total ← 10, new ← {'n': 'a', 'p': 2, 'q': 5, 'total': 10}
pass 1 of 610enriched = []11for r in records:12 total = r['p'] * r['q']13 new = {**r, 'total': total}14 enriched.append(new)15print('RESULT:', [d['total'] for d in enriched])values this step{'n': 'a', 'p': 2, 'q': 5}r10total{'n': 'a', 'p': 2, 'q': 5, 'total': 10}newAll 6 passes — pass 1 is the card above pass rtotalnew1 {'n': 'a', 'p': 2, 'q': 5} 10 {'n': 'a', 'p': 2, 'q': 5, 'total': 10} 2 {'n': 'a', 'p': 2, 'q': 5} → {'n': 'b', 'p': 5, 'q': 6} 10 → 30 {'n': 'a', 'p': 2, 'q': 5, 'total': 10} → {'n': 'b', 'p': 5, 'q': 6, 'total': 30} 3 {'n': 'b', 'p': 5, 'q': 6} → {'n': 'c', 'p': 3, 'q': 7} 30 → 21 {'n': 'b', 'p': 5, 'q': 6, 'total': 30} → {'n': 'c', 'p': 3, 'q': 7, 'total': 21} 4 {'n': 'c', 'p': 3, 'q': 7} → {'n': 'd', 'p': 8, 'q': 3} 21 → 24 {'n': 'c', 'p': 3, 'q': 7, 'total': 21} → {'n': 'd', 'p': 8, 'q': 3, 'total': 24} 5 {'n': 'd', 'p': 8, 'q': 3} → {'n': 'e', 'p': 4, 'q': 8} 24 → 32 {'n': 'd', 'p': 8, 'q': 3, 'total': 24} → {'n': 'e', 'p': 4, 'q': 8, 'total': 32} 6 {'n': 'e', 'p': 4, 'q': 8} → {'n': 'f', 'p': 6, 'q': 9} 32 → 54 {'n': 'e', 'p': 4, 'q': 8, 'total': 32} → {'n': 'f', 'p': 6, 'q': 9, 'total': 54} for r in records:
10enriched = []11for r in records:12 total = r['p'] * r['q']stdout ← RESULT: [10, 30, 21, 24, 32, 54]
14 enriched.append(new)15print('RESULT:', [d['total'] for d in enriched])values this stepRESULT: [10, 30, 21, 24, 32, 54]stdout
The Pythonic way
A list comprehension with the dict-spread idiom {**r, 'total': r['p'] * r['q']}
produces each enriched record in one expression, without an explicit loop or
accumulator.
library.py
records = [
{'n': 'a', 'p': 2, 'q': 5},
{'n': 'b', 'p': 5, 'q': 6},
{'n': 'c', 'p': 3, 'q': 7},
{'n': 'd', 'p': 8, 'q': 3},
{'n': 'e', 'p': 4, 'q': 8},
{'n': 'f', 'p': 6, 'q': 9},
]
enriched = [{**r, 'total': r['p'] * r['q']} for r in records]
print('RESULT:', [d['total'] for d in enriched])
RESULT: [10, 30, 21, 24, 32, 54]
Implementation notes
recordsandenrichedare excluded from tracing (# trace: ignore) because even two full-dict records push the list repr past 80 characters. The per-step visible state isr(the input record, ~25 chars),total(the computed value), andnew(the enriched record with the added field, ~40 chars) — exactly the three variables that teach "add a derived field".{**r, 'total': total}is the standard idiom for non-destructive dict enrichment: it copies every key fromrinto a new dict and adds'total'.- The
enriched.append(new)events are zero-delta in the trace becauseenrichedis ignored; the RESULT line confirms the accumulated output.