1.4.12 · D5Python & Scientific Computing

Question bank — Reading documentation and debugging

1,406 words6 min readBack to topic

Before we start, two words we use constantly, defined from zero:


True or false — justify

The default of numpy.linspace gives you the same numbers as range(start, stop)
False. linspace includes the stop endpoint (endpoint=True) and returns floats spaced evenly; range excludes its stop and only yields integers. Different contracts entirely.
re.split(r'\d+', 'a1b2') returns the digits it split on
False by default — it returns ['a', 'b', ''], discarding delimiters. Only if you wrap the pattern in a capturing group (\d+) are the split-on pieces also returned.
Calling df.groupby('col') immediately gives you the averaged result
False. groupby returns a lazy DataFrameGroupBy object; nothing is computed until you call an aggregator like .mean() or .sum() on it.
By default, rows whose grouping key is NaN still appear as their own group
False. The default dropna=True drops rows with NaN in the grouping key entirely; you must pass dropna=False to keep them as a group.
You should read a Python traceback from top to bottom
False. Read it bottom-to-top: the last line is the exception type and message (the actual failure), and the frames above it, read upward, trace how execution arrived there.
Binary-search debugging requires the bug to be in a sorted list
False. "Binary search" here refers to the search over lines of code: check the middle point of the suspect range and halve the possibilities each test. It has nothing to do with sorted data.
If print(len(students)) shows a non-zero number, division by zero is fully ruled out for total / len(students)
True for that call, but only that call — a later call could still pass an empty list. Ruling out one input does not prove the code is safe for all inputs.
A function documented as returning "an ndarray" always returns an ndarray
Not necessarily — check the conditions. E.g. linspace(..., retstep=True) returns a (array, step) tuple instead. The return type can depend on argument values, which is exactly why the Returns section matters.
Reading the "See Also" section is optional fluff
False in spirit — "See Also" answers "what else could I use?", which is half of choosing correctly (e.g. groupby vs pivot_table, linspace vs arange). Skipping it is how you pick the wrong tool.

Spot the error

for s in students: total = 0; total += s['score'] — what's wrong?
The initialization total = 0 sits inside the loop, so it resets every iteration; the sum only ever reflects the last student. Move total = 0 above the loop.
re.split('(\d+)', 'a1b2') in a plain string literal — subtle bug?
\d in a normal string can trigger escape-sequence warnings; use a raw string r'(\d+)' so the backslash reaches the regex engine untouched.
A dev fixes a KeyError: 'price' by wrapping the line in try/except: pass. Why is this wrong?
It hides the symptom without finding the cause — the missing 'price' key means upstream data is malformed. Silently passing lets corrupt records flow downstream and fail later, harder to trace.
np.linspace(0, 10, 10) is used to get integers 0..10. What's the mistake?
num=10 gives 10 points, not 11, and they're floats with non-integer spacing (10/9 ≈ 1.11). To get integers 0..10 use np.arange(0, 11) or linspace(0, 10, 11).
Debugging by randomly editing lines until the output looks right — name the flaw.
There's no hypothesis being tested, so a "fix" may just mask the bug or shift it. You lose the ability to reason about why it now works, and it often silently breaks another case.
df.groupby('city').mean() errors with "no numeric types to aggregate". Cause?
.mean() needs numeric columns; if the non-key columns are strings, there's nothing to average. Select numeric columns first or use an aggregation valid for that dtype.

Why questions

Why read the one-line purpose before the parameter list?
The purpose answers "should I even be using this tool?" — if it solves the wrong problem, no amount of parameter tuning helps. It filters before you invest reading effort.
Why does binary-search debugging cost about tests instead of ?
Each test on the middle line discards half the remaining suspect lines: . Halving repeatedly reaches one line in about steps, versus checking lines one by one which averages .
Why does a good doc-reader build a "decision tree" of related functions rather than memorizing one?
Real problems land on the boundaries between functions (split vs partition vs re.split). Knowing when each applies lets you pick correctly the first time instead of forcing the one you happen to know.
Why is groupby designed to be lazy (returning an object, not a result)?
So you can attach any aggregation (.mean, .sum, .agg(...)) to the same grouping without recomputing the groups each time — separating "how to split" from "what to compute" is efficient and flexible.
Why read the exception message, not just the exception type?
The type (KeyError) tells you the category; the message tells you the specific offender ('price'), which is what narrows the search to a concrete cause.
Why does endpoint=True even exist as a knob on linspace?
Sometimes you want the interval closed (include stop, e.g. plotting a full curve), sometimes half-open (exclude stop, e.g. tiling segments without overlap). The knob lets one function serve both contracts.

Edge cases

What does total / len(students) do when students is empty?
It raises ZeroDivisionError — the loop never runs, total stays 0, and len is 0. Empty input is the classic boundary the average-function forgets; guard with an explicit empty check.
What does np.linspace(5, 5, 4) return?
Four copies of 5.0[5., 5., 5., 5.]. When start == stop the spacing is zero; it's degenerate but not an error.
With re.split(r'(\d+)', 'a1'), why is the last element an empty string ''?
The pattern matches at the very end, so the "after" piece is empty. split faithfully reports the (possibly empty) text between and after every match — always account for trailing empties.
If a grouping column has both NaN and a real category, what happens under default groupby?
The NaN-key rows are silently dropped (dropna=True), so their contribution vanishes from every aggregate — a quiet data-loss trap. Pass dropna=False to keep them.
What does a traceback look like when the error happens inside a list comprehension?
You'll see an extra frame labelled <listcomp> between the calling function and the failing expression, pinpointing that the exception arose while iterating — read it as "this line, during the comprehension".
What return type do you get from linspace(0, 1, 5, retstep=True)?
A tuple (array, step), not a bare array — the presence of retstep=True changes the shape of the return, so unpacking as arr = linspace(...) would leave arr holding the whole tuple.
Recall One-line survival rules

Docs before code, exception message before guess, middle-line before random edit, and always ask "what happens on empty / zero / equal / NaN?" ::: These four habits collapse most debugging sessions from hours to minutes.

Related builds: 1.4.1-Python-basics-syntax-and-variables (the loop-scope trap), 1.4.5-File-IOand-CSV-handling and 1.7.2-Data-preprocessing-and-cleaning (where NaN-key drops bite), 1.4.10-Introduction-to-matplotlib (linspace for plotting), 2.3.4-Hyperparameter-tuning-grid-search-random-search (reading library signatures under pressure).