Matheion

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: start inclusive, stop exclusive. Omit start (from beginning) or stop (through end): nums[:3], nums[2:].
  • step optional: 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 append vs += [list]vsextend** on a starter list **[1]`.
  • Know why b = a; b.reverse() reverses a as 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. 1.What does this print? ```python a = [1, 2, 3] b = a b.append(4) print(a) ```

  2. 2.After running: ```python a = [1, 2, 3] c = a + [4] ``` what is `a`?

  3. 3.What is the value of `[n * n for n in range(1, 4)]`?

  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. 5.What does `result = [1, 2].append(3)` bind `result` to?