Values in an array are wrong — zeros, scaled numbers, or shuffled entries you never put there — yet no line of code assigns to that array, and nothing crashed. The corruption often surfaces far from its cause: a report prints wrong totals, and the array was "damaged" two functions earlier.
Any code that takes a slice of an array and then modifies it: normalising a window
of readings, zeroing a test region, scaling one column in place. Common in module
0.4 exercises and in every real cleaning pipeline afterwards. The same trap in list
form appeared in WR-008 (b = a aliasing) — this is its NumPy cousin, wearing a
slice as a disguise. Linked module: 0.4 (NumPy).
Basic slicing (a[2:5], a[:, 0], a[0, :3]) returns a view — a new name looking
at the SAME memory as the parent array. NumPy does this for speed: no copying, ever,
until you ask. So w = grid[0, :3] followed by w[:] = 0 writes zeros straight
into grid. The confusing part: boolean-mask indexing (a[a > 5]) and fancy
indexing (a[[0, 2]]) return copies, so the bug appears and disappears depending on
HOW you indexed. Confirm the relationship with np.shares_memory(w, grid) — True
means they overlap — or check w.base is grid. Then search for every place the
suspect slice is modified: in-place assignment (w[:] = ...), or in-place operators
(w += 1, w *= 2).
Decide the intent, then make it explicit. If you wanted an independent piece, take
one: w = grid[0, :3].copy() — modifications now stay local. If you wanted in-place
editing of the parent, keep the view but say so in the code with a comment, and move
the modification next to the slice so no reader is surprised. Re-run the pipeline
from a fresh load of the data afterwards — the damaged array in memory stays damaged
until you rebuild it, and re-running only the later steps re-uses corrupted values.
Copy at ownership boundaries: any slice that leaves a function, or is stored for
later, should be a .copy(). Treat views as read-only unless the in-place edit is
the whole point. When a value is wrong "impossibly", ask np.shares_memory before
blaming the maths. In code review, flag every slice-then-modify pair and make the
author state view-or-copy out loud.
Q: "What is the difference between a view and a copy in NumPy, and when does it
bite?" Strong answer: basic slices are views into shared memory, mask and fancy
indexing return copies, and the bite is silent corruption of the parent after an
in-place write. Name np.shares_memory (or .base) as the proof tool and
.copy() at boundaries as the discipline — interviewers read that pairing of
diagnosis plus habit as real debugging experience.