11 · Lists: the workhorse sequence
Indexing, slicing, the full mutation API, and the subtle reference-not-copy trap that bites every new Python programmer.
A Python list is an **ordered, mutable** sequence — it can hold any mix of types and grow or shrink at runtime.
Without this:
Without lists you can't accumulate training metrics, hold a batch of samples, or represent a feature vector before NumPy — every repeated data pattern requires a separate variable.
A list literal is written with square brackets: [1, 2, 3]. Indexing uses list[i] (0-based; negative indices count from the end). Slicing returns a new list: list[start:stop:step] — stop is exclusive. Lists are mutable — unlike strings, you can change individual elements in place.
Python (in browser)
All these mutations happen in place — no new list is returned.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Two methods sort a list in different ways. .sort() mutates the list in place and returns None. sorted() is a built-in that leaves the original list untouched and returns a new sorted list. The same asymmetry applies to .reverse() vs reversed().
Python (in browser)
`sorted()` is non-destructive and works on any iterable. `.sort()` is in-place and list-only. Always print the return value of `.sort()` once — seeing `None` makes the pattern stick.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
Assignment copies the *reference*, not the data. Use `.copy()` or `a[:]` to get an independent shallow copy.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
What does `[1, 2, 3].sort()` return?
Practice list indexing, slicing, and the full mutation API in the Arena with server-side tests.
A list of numbers is Python's closest built-in to a mathematical vector. MML chapter 2 covers vectors as ordered tuples in Rⁿ — the same idea `[x₁, x₂, ..., xₙ]` you'll soon hand off to NumPy.
- Lists are ordered and mutable — index with `list[i]`, slice with `list[a:b]`, mutate with `.append()`, `.pop()`, `.insert()`, `.remove()`, `.extend()`.
- `.sort()` mutates in place and returns `None`; `sorted()` returns a new sorted list. Never assign `my_list = my_list.sort()`.
- `b = a` creates an alias, not a copy. Use `b = a.copy()` or `b = a[:]` to get an independent list.
Training loss is accumulated as `history = []; history.append(epoch_loss)` after each epoch. Mini-batches are slices of a feature list: `batch = dataset[i:i+batch_size]`. Feature arrays are built as plain lists before being handed to NumPy: `features = [row['age'], row['income'], row['credit_score']]`.
If you remove it: Without lists, accumulating metrics over epochs requires pre-allocating a fixed-size structure — you'd need to know in advance how many epochs you'll run, which defeats the purpose of adaptive training.