Python Coding · Cheat Sheet
Reading Functions and Notebooks
Cheatsheet: Reading Functions and Notebooks
One page for Topic 3 — def blocks, the one Python landmine worth memorizing, and how to audit a notebook like an auditor.
The function-reading checklist
Read every def block in four parts:
| Part | What it is | How to read it |
|---|---|---|
| Name | def clean_data(...) | A claim by the author, not a guarantee — the body is the fact |
| Parameters | The inputs | Inside the function, df means whatever was passed in, not any particular frame in the notebook |
| Defaults | min_revenue=0 | Invisible decisions — a short call like analyze(sales) silently runs with every default; always read the def line to know what a short call really passes |
return | The official product | Anything else it does to the outside world is a side effect |
A def cell running teaches Python the recipe; the code executes at each call. The fastest verification: trace one call by hand with five concrete rows — thirty seconds tells you more than any amount of reading the name.
The key question: change or copy?
| Signal | Meaning |
|---|---|
inplace=True, or df['col'] = ... inside the body | Mutates the caller's DataFrame — the original changes even if the return value is ignored |
.copy() near the top; builds and returns new frames | Works on a copy — original untouched |
Call site df = f(df) | Pairs with copy-style functions |
Bare f(df) on its own line | Only meaningful for a mutating function — with a copy-style function it's a silent no-op that looks like a cleaning step |
A mismatch between function style and call style is a bug you can spot without running anything. Pure functions (compute and return only) are dramatically easier to audit — a good standing instruction for AI: "prefer functions that return a new DataFrame rather than modifying inputs."
Scope: the global-df trap
A function with no parameters that reads df from the surrounding notebook grabs whatever df is at call time — re-load or re-filter between calls and the same call returns different answers. A DataFrame used but not passed as a parameter is a dependency invisible at the call site. Flag it.
Mutable default arguments
Rule: a default value is evaluated once — at def time — not once per call. Every call that omits the argument shares the same object.
def collect(value, results=[]):
results.append(value)
return results
collect(10) # [10]
collect(20) # [10, 20] ← the function has memory
| Def line | Verdict |
|---|---|
def f(x, results=[]) | Red flag — shared list accumulates across calls |
def f(x, cache={}) | Red flag — same mechanism |
def f(x, min_revenue=0) / region='West' | Safe — immutable defaults can't accumulate |
def f(x, results=None) + if results is None: results = [] | The safe idiom — fresh list created inside each call |
Notebook symptom: metrics that double on every cell re-run but look perfect after a kernel restart. The def cell doesn't re-run, so the default list still holds last run's rows; a rerun appends again — totals exactly 2x, then 3x. Invisible on a fresh run, which is the cruelest property.
Sibling bug: Python passes objects by reference — a function can silently mutate a passed-in DataFrame or list (df['margin'] = ... welds a column onto the caller's frame). Defensive code copies first (df = df.copy()). If a function's job is to compute, verify it didn't also edit — compare the input's shape before and after the call.
Notebook audit
A notebook's displayed outputs are claims about a past session, not properties of the code you're reading.
Execution counters: the flight recorder
In [4]= this cell was the 4th run this kernel session.[ ]= never run; any saved output is archaeological.- Cells display top to bottom but executed in click order. Counters that don't increase down the page mean some outputs are stale — a sum with
[3]sitting below a filter with[5]ran before the filter and includes what the filter excludes.
Hidden state warning
The kernel remembers variables from cells that were edited or deleted after running — no trace on the page, full effect in memory. Combined with out-of-order execution and the mutable-default rerun bug: the page and the session drift apart silently.
Restart and Run All is the only trustworthy state. Afterward, counters go 1, 2, 3... and every output provably came from the code above it. A notebook that errors under Restart and Run All is a finding, not an inconvenience — it can't reproduce its own claims.
The four-stage audit order
| Stage | Question | Check |
|---|---|---|
| 1. Imports + data load | What came in, how many rows? | len(df) / df.info() at load — your baseline |
| 2. Cleaning | What was dropped or altered, and how many? | Every dropna/filter/dedup accounts for its damage; verify the condition afterward, not the printed narration |
| 3. Transformation | Merges and groupbys sound? | Row counts before/after each merge; grouped totals reconciled against ungrouped; ratio columns checked for mean-of-ratios |
| 4. Conclusion / output | Does the prose match the output? | Physically find the headline number in the last output cell. Markdown never re-executes — stale conclusions over fresh data are routine |
Traps
- Trusting the function name —
remove_outlierswith subtly different logic in the body; AI names are especially polished. - Missing the defaults — every default is a decision the caller didn't visibly make.
- A discarded return value — bare
clean(df)wherecleanreturns a copy is a no-op dressed as a cleaning step. - Hidden mutation — a "summary stats" function that also does
dropna(inplace=True)on the way through. =[]or={}in a def line — the bug, or one rerun away from it; the fix is=None+if ... is None.- AI patching the symptom —
drop_duplicates()bolted on to mask doubled totals leaves the landmine armed. - Validating on a fresh run only — the double-counting bug hides after every kernel restart; the state your analyst actually had on screen matters too.
- Treating displayed output as current — it's a record of the last run, under whatever state existed then; check counters first.
- Trusting markdown narration — prose describes intent at writing time; code got edited, markdown never re-ran.
- Skipping the last-mile check — output says 58.0, conclusion says $41: the cheapest bug to catch in all of analytics, and genuinely common.
Aligned to the official Python and pandas documentation.
Make this cheat sheet yours
Personalize this sheet — focus it however you study, or build one from the exact questions you keep getting wrong.