MA-PY1 · Module 1.4
Lists, indexing, and mutability
Topics: Lists · Indexing · Mutation
Overview
Module 1.4 — Lists, indexing, and mutability
Scroll Core ideas → worked snippets → Practice (quiz). Attempt each question before Discuss / reveal. Use any Python REPL, script, or notebook you like.
Concepts
Core ideas — lists: ordered, flexible, mutable
The list is Python’s general-purpose sequence: ordered, indexed from zero, growable, and mutable (you can change elements in place). Almost every algorithm text eventually maps “array / dynamic array” intuition here.
Indexing and slices
- Valid indices run
-len .. len-1. Negative indices count from the end:items[-1]is last,items[-2]second-to-last. - Slicing
seq[start:stop]selects a half-open range:startinclusive,stopexclusive. Omitstart(from beginning) orstop(through end):nums[:3],nums[2:]. stepoptional:s[::-1]reverses copies (builds new list).
Out-of-range indexing (a[99]) raises IndexError; slicing is forgiving—it clips to available elements.
Core mutation operations
Understand both meaning and return value:
| Operation | Effect |
|-----------|--------|
| L.append(x) | Adds x at end (in place); returns None—don't assign its result |
| L.extend(iter) | Appends each element of iter one by one |
| L.pop() / L.pop(i) | Removes and returns that element |
| L.insert(i, x) | Inserts x before index i (shifts right) |
+ on lists concatenates but allocates a new list (a + b leaves a unchanged unless you reassign names).
Aliasing versus copying
Assignment never clones data:
a = [1, 2, 3]
b = a # alias — same object
Shallow copy: b = list(a) or b = a[:]. Nested lists share inner objects unless you copy.deepcopy (later topic)—for flat lists list(a) is enough.
List comprehensions
Syntax: [expression for target in iterable if filter].
They always build a new list; they excel at transformations + filters in one readable line—but if the body needs many statements prefer a for loop first, then refactor when stable.
When to mutate vs rebuild
Performance aside: mutate when you deliberately reuse one buffer (streaming aggregation); rebuild when purity matters or you snapshot old state (sorted(nums) vs nums.sort()—first returns new, second sorts in place).
Self-study checklist
- Draw before/after memory pictures for
appendvs+=[list]vsextend** on a starter list **[1]`. - Know why
b = a; b.reverse()reversesaas well (same object). - Produce half-open slice equivalents for “first
k” / “everything but last”.
Examples
Worked snippets — lists from scratch to fluent
Indices and negatives
fib = [0, 1, 1, 2, 3, 5]
print(fib[0], fib[-1])
print(len(fib))
Slicing gymnastics
nums = [10, 20, 30, 40]
print(nums[1:3]) # [20, 30]
print(nums[:2]) # first two
print(nums[::-1]) # reversed copy
Clone idiom: copy = nums[:] (shallow).
Mutation vs concatenation
xs = [1]
xs.append(2)
print(xs)
xs = xs + [3] # NEW list rebound to xs — previous [1,2] may be orphaned
Extend vs nested append mistake
a = [1, 2]
a.extend([8, 9]) # → [1, 2, 8, 9]
b = [1, 2]
b.append([8, 9]) # → [1, 2, [8, 9]] — ONE extra element!
List comprehension essentials
nums = [1, 2, 3, 4, 5]
squares = [n * n for n in nums]
evens_sq = [n * n for n in nums if n % 2 == 0]
print(squares, evens_sq)
Equivalent loop skeleton for evens_sq:
evens_sq = []
for n in nums:
if n % 2 == 0:
evens_sq.append(n * n)
Sorted vs sort
data = [3, 1, 4, 1, 5]
print(sorted(data)) # new list, data untouched
mut = list(data)
mut.sort()
print(mut)
Pop and stack discipline
stack = []
stack.append('a'); stack.append('b')
while stack:
print(stack.pop())
Treat append + pop()` end as stack ADT when reading algorithm explanations.
Practice check — lists, indexing, and mutability
Multiple-choice check from the Matheion app lesson. Pick an answer for each question, then submit.
1.What does this print? ```python a = [1, 2, 3] b = a b.append(4) print(a) ```
2.After running: ```python a = [1, 2, 3] c = a + [4] ``` what is `a`?
3.What is the value of `[n * n for n in range(1, 4)]`?
4.You want an independent copy of a list so changes to the copy do not affect the original. Which makes a (shallow) copy?
5.What does `result = [1, 2].append(3)` bind `result` to?