Worked examples — Jupyter notebooks workflow
Before anything: one word we will use on every line.
The scenario matrix
Every confusing thing Jupyter does is one of these case-classes. The examples below hit every row.
| # | Case class | What varies | Hit by |
|---|---|---|---|
| A | Top-to-bottom order | cells run 1,2,3 as written | Ex 1 |
| B | Out-of-order execution | a later cell runs before an earlier one | Ex 2 |
| C | Redefinition / shadowing | same name written twice | Ex 3 |
| D | Degenerate: name never defined | reading a name with no arrow | Ex 4 |
| E | Reset: kernel restart | namespace wiped to empty | Ex 5 |
| F | Mutation vs re-assignment | object changed in place vs replaced | Ex 6 |
| G | Deletion: del removes an arrow |
a name is explicitly deleted | Ex 7 |
| H | The "works-for-me" trap | saved outputs vs live namespace | Ex 8 |
| I | Real-world word problem | a real analysis, predict R² | Ex 9 |
| J | Exam-style twist | predict every In [n] from a scramble |
Ex 10 |
A quick vocabulary note so the taxonomy stays clean: throughout this page "limiting behaviour" means only the edge/extreme end of a spectrum (an empty namespace, a zero input) — we reserve it for Case E's restart-to-empty and Case D's empty-slot read. It is never a case name.
Example 4 — Case D (degenerate): reading a name that was never written
In [1]: total = price * quantitywhere you thought you had defined price and quantity earlier but never did (or you restarted the kernel and forgot to re-run those cells).
Forecast: what exactly does the error say, and which name is blamed?
- Cell runs. Why this step? Python evaluates the right side left-to-right; the very first unknown name it meets is
price, and the namespace has no arrow for it — this is the degenerate empty-slot read. - Error raised. Why this step? Because evaluation halts at the first failure, Python reports
NameError: name 'price' is not defined— it namesprice, notquantity, since it never got that far.
Verify (sanity check): This is the empty-input / degenerate limiting case — reading from a blank slot. The fix is "Run All Above" (parent's five operations table) to fill the missing arrows. Units: none — this errors before any arithmetic happens.
Example 5 — Case E (reset): kernel restart wipes the namespace
In [1]: data = [10, 20, 30]
In [2]: s = sum(data) # s = 60
--- you press 0 0 : RESTART KERNEL ---
In [1]: print(s)Forecast: after restart, what happens to print(s)?
- Before restart. Why this step? We record the starting state so the contrast is visible: namespace is .
- Restart. Why this step? Restart kills the Python process and starts a fresh one → namespace becomes . Every arrow is deleted. The counter also resets, so the next cell is
In [1]again. print(s). Why this step? Readingsfrom the now-empty namespace shows the consequence:NameError: name 's' is not defined.
The figure below stages this as two boxes with a magenta "press 0 0" arrow between them. Compare the two boxes: the left violet box is full (), the right dashed orange box is empty ({ }) — the arrow between them is the restart, and the caption underneath is the resulting NameError. The picture makes concrete that the 60 lived only inside the left box and vanished with it.

Verify: The value had been , but that number lived only in memory, never re-computed after restart. This is the limiting behaviour: restart drives the namespace to its empty extreme, identical to launching a brand-new notebook.
Example 6 — Case F: mutation vs re-assignment (the subtle one) Two cells that look almost identical but behave oppositely:
In [1]: a = [1, 2, 3]
In [2]: b = a # b points to the SAME list object
In [3]: a.append(4) # MUTATE the object in place
In [4]: print(b)Forecast: does b show [1,2,3] or [1,2,3,4]?
- Cells 1–2. Why this step?
b = adoes not copy; both names arrow to the one list object: , . We set up the shared arrow so the aliasing is visible. - Cell 3. Why this step?
.append(4)changes the object itself (mutation); it does not create a new arrow, so both names still point at the same, now-longer list: is[1,2,3,4]. - Cell 4. Why this step? We read
bto prove the surprise: it still arrows to , so it prints[1, 2, 3, 4]even though we only toucheda.
Now contrast re-assignment:
In [5]: a = [1, 2, 3] # brand-new object, a re-pointed
In [6]: print(b) # b still holds the OLD mutated list- Cell 5–6. Why this step?
a = [...]builds a new object and re-points onlya;b's arrow is untouched → still[1,2,3,4]. This isolates re-assignment (moves an arrow) from mutation (edits the object).
Verify: After cell 4, b == [1,2,3,4] (length 4). After cell 6, b is unchanged, still length 4. This is the out-of-order trap that survives even correct execution order — track objects, not just names. See 1.4.10-NumPy-arrays-basics where the same aliasing bites arrays.
Example 7 — Case G: del explicitly removes an arrow
In [1]: temp = 99
In [2]: del temp
In [3]: print(temp)Forecast: after del temp, does print(temp) still show 99, or something else?
- Cell 1. Why this step? We create an arrow so there is something to delete: .
- Cell 2 (
del temp). Why this step?deldoes not change a value — it erases the arrow itself, unlike assignment which redirects an arrow. Namespace becomes (for this name). - Cell 3. Why this step? Reading
tempafter its arrow is gone reproduces the exact Case-D situation on purpose:NameError: name 'temp' is not defined.
Verify: The value 99 is unrecoverable — del deletes the name, and once the last arrow to an object is gone Python can reclaim the object. Re-running Cell 1 (which writes temp→99 again) is the only way print(temp) returns to 99. Sanity check: del on an already-deleted name raises the same NameError, confirming the arrow is truly gone. See 4.1.05-Debugging-strategies.
Example 8 — Case H: "works for me, breaks for them" Your notebook on disk, as the reader sees it top-to-bottom:
Cell 1: result = base_rate + bonus
Cell 2: base_rate = 100
Cell 3: bonus = 25But you had run 2, then 3, then 1, so it worked for you and you saved a nice 125 output.
Forecast: what happens when your collaborator hits "Restart & Run All"?
- Restart. Why this step? "Restart & Run All" begins from the empty namespace — the whole point is to simulate a fresh reader, so no leftover arrows can rescue the code.
- Cell 1 runs first (top-to-bottom). Why this step? Because the reader runs in file order, the first cell executed reads
base_rate— which has no arrow yet →NameErroron line one. The analysis dies immediately.
Verify: The saved output shows 125 (a real value: ), yet a clean run produces an error. Saved outputs are a photograph of the last run, not a recipe. The fix from the parent: always finish with Restart & Run All before sharing. is the number they'll never see until the cells are reordered 2,3,1.
Example 9 — Case I: real-world word problem
You load an ad-spend dataset and build the model from the parent's Example 1, splitting into clean cells. Suppose after fitting LinearRegression on features ['ad_spend', 'season'] the true relationship in your data is exactly
with no noise, over these 4 rows:
| ad_spend | season | log_revenue |
|---|---|---|
| 2 | 1 | |
| 4 | 0 | |
| 6 | 1 | |
| 8 | 0 |
Forecast: what R² will model.score(X, y) print?
Two words we need first. The residual sum of squares (RSS) is the total of squared gaps between the model's predictions and the true values — how much the model misses by. The total sum of squares (TSS) is the total of squared gaps between the true values and their own average — how much the data varies to begin with. Then .
- Build the true
ycolumn. Why this step? We generate targets from a perfectly linear rule, so a linear model can fit it exactly with zero miss. Values: . - Fit and score. Why this step? A perfect fit makes every prediction land on its true value, so RSS ; plugging into gives .
Verify: RSS is 0, TSS is positive, so . Units: is dimensionless (a ratio of variances). See 2.3.01-Exploratory-data-analysis and 1.5.01-Pandas-DataFrames.
Example 10 — Case J: exam twist, reconstruct every In [n]
A grader shows you a notebook whose cells display these counters:
Cell A: In [3] x = 1
Cell B: In [1] y = 10
Cell C: In [4] print(x + y)
Cell D: In [2] y = y + 5
Forecast: in what order were the cells actually run, and what did Cell C print?
- Sort by counter. Why this step? The counter increments once per run, so ascending order is the run order — it is the only reliable record of history:
In [1]→B,In [2]→D,In [3]→A,In [4]→C. Run order = B, D, A, C. - Replay the namespace. Why this step? We now walk the cells in that recovered order, updating arrows each time, because only the true order gives the true final values:
- B:
y=10→ - D:
y = y + 5reads 10, writes 15 → - A:
x = 1→ - C: prints .
- B:
Verify: . The trap: reading top-to-bottom you'd wrongly guess y=10 and answer 11. The counters, not the layout, tell the truth.
Recall
Case A — in an honest run, what pattern do the In [n] numbers show? ::: They strictly increase (1,2,3…) — the sign nobody ran cells out of order.
Case B — smaller In number below a larger one means what? ::: The notebook was run out of order (a reproducibility bomb).
Case C — after n=3; n=n+7; n=n*2, what is n? ::: 20 — each cell reads the current arrow then overwrites it.
Case D — NameError blames which name when several are missing? ::: The first undefined name Python meets, left-to-right.
Case E — after Restart Kernel, what does the namespace contain? ::: Nothing — it is emptied to and the counter resets to In [1].
Case F — does b = a copy the list? ::: No; both names point to the same object, so mutating through a shows up in b.
Case G — what does del temp remove, the value or the arrow? ::: The arrow (the name); reading temp afterwards raises NameError.
Case H — saved outputs are a recipe or a photograph? ::: A photograph of the last run — not a reproducible recipe. Always Restart & Run All before sharing.
Case I — with a perfect noiseless linear fit, what is R²? ::: Exactly 1.0, because RSS = 0.
Case J — how do you recover true run order? ::: Sort cells by ascending In [n] counter.
"A BeD For Great Honest Ideas, Judge" — one word per case in order: A (top-to-bottom), B (out-of-order), Define-again = redefinition (C), Fresh-slot missing = degenerate (D), For ≈ reset/restart (E), Glue = mutation aliasing (F), Gone = deletion del (G), Honest-trap = works-for-me (H), Ideas = real-world word problem (I), Judge = exam twist (J). If the letters feel forced, fall back on the plain rule: sign / order / redefine / undefined / reset / mutate / delete / share-trap / real-problem / exam-twist.
Back to the parent: Jupyter notebooks workflow · prerequisite 1.4.08-Virtual-environments · next steps 1.6.01-Matplotlib-basics.