Exercises — List methods — append, insert, remove, pop, sort, reverse, count, index
Before we begin, one picture to keep in your head — a list is a row of numbered slots, and every method is a move on this row.

Read the figure like this: the cyan slots are positions (indices start at ). The amber arrows show what each family of methods does — grow the row, shrink it, flip it, or just point at it and ask a question.
Level 1 — Recognition
Goal: state what a single method returns and whether it mutates.
Exercise 1.1
What does each line return (the value handed back), and what is nums after each line runs independently on a fresh nums = [3, 1, 2]?
nums.append(9)
nums.sort()
nums.pop()
nums.count(1)Recall Solution 1.1
"Return" = the value the expression evaluates to. "After" = the state of the list.
nums.append(9)→ returns ==None==; list becomes[3, 1, 2, 9]. (Adds to the end, mutates in place.)nums.sort()→ returns ==None==; list becomes[1, 2, 3]. (Reorders in place.)nums.pop()→ returns ==2== (the last item); list becomes[3, 1]. (Removes and hands it back.)nums.count(1)→ returns ==1==; list unchanged[3, 1, 2]. (Just asks a question — never mutates.)
Key split: pop, count, index give you a useful value back; append, sort, insert, remove, reverse give back None.
Exercise 1.2
Match each method to its family (Adder / Taker / Mover / Asker): insert, reverse, index, remove.
Recall Solution 1.2
insert→ Adder (puts a new item in).reverse→ Mover (reorders, adds/removes nothing).index→ Asker (returns a position, changes nothing).remove→ Taker (deletes an item by value).
Level 2 — Application
Goal: trace a sequence of mutations and track both the list and returned values.
Exercise 2.1
Trace this program. Give the value of a, b, and the final nums.
nums = [5, 3, 5, 1]
nums.insert(2, 8) # step 1
a = nums.pop(0) # step 2
b = nums.pop() # step 3
nums.remove(5) # step 4Recall Solution 2.1
- Step 1
insert(2, 8): index 2 held5; everything at index shifts right one. →[5, 3, 8, 5, 1]. - Step 2
a = nums.pop(0): removes and returns the front item.a = 5; list →[3, 8, 5, 1]. - Step 3
b = nums.pop(): no argument means last item.b = 1; list →[3, 8, 5]. - Step 4
remove(5): searches by value, deletes the first5. →[3, 8].
Answers: a = 5, b = 1, nums = [3, 8].
Exercise 2.2
Start with data = [4, 2, 4, 1]. After data.sort() then data.reverse(), what is data.index(4) and data.count(4)?
Recall Solution 2.2
sort()→[1, 2, 4, 4](ascending).reverse()→[4, 4, 2, 1](flip, does not re-sort).index(4)→ returns the first position of4, which is0.count(4)→2.
Answers: index(4) = 0, count(4) = 2.
Level 3 — Analysis
Goal: explain why a line behaves as it does, and diagnose bugs.
Exercise 3.1
This code raises an error. Which one, on which line, and why?
box = [5, 6, 7]
box.remove(0)Recall Solution 3.1
remove takes a value, not an index. There is no value 0 inside [5, 6, 7], so Python raises a ==ValueError== on the box.remove(0) line.
The trap: someone reading it thinks "remove index 0." That is pop(0), which would return 5. See Exceptions — ValueError and IndexError.
Exercise 3.2
Predict the printed value, then explain the surprise:
lst = [8, 3, 6]
result = lst.sort()
print(result)
print(lst)Recall Solution 3.2
lst.sort()mutateslstto[3, 6, 8]and returnsNone.result = None, soprint(result)printsNone.print(lst)prints[3, 6, 8].
The data is not lost — it was sorted in place; only the return value is None. This is the assignment bug from L1, seen live.
Exercise 3.3
Why does [1, 'a'].sort() fail while ['b', 'a'].sort() succeeds?
Recall Solution 3.3
sort() orders items by repeatedly asking "is x < y?". Python knows how to compare str with str ('a' < 'b') and int with int, but not int with str. So [1, 'a'].sort() raises a ==TypeError==, while ['b', 'a'].sort() → ['a', 'b']. Keep list items to one comparable type. See Mutability vs Immutability in Python for why lists let you do all this in place.
Level 4 — Synthesis
Goal: combine several methods to build a target result.
Exercise 4.1
Given raw = [4, 2, 4, 1, 2, 4], write a sequence that produces [4, 2, 1] — the distinct values in descending order — using only list methods and querying with count. Then state the final list.
Recall Solution 4.1
One clean route:
raw = [4, 2, 4, 1, 2, 4]
raw.sort() # [1, 2, 2, 4, 4, 4]
out = []
for x in raw:
if out.count(x) == 0: # not seen yet
out.append(x) # out builds up distinct values ascending
out.reverse() # descending- After the loop,
out = [1, 2, 4](each value appended once, ascending becauserawwas sorted). reverse()→[4, 2, 1].
Final: out = [4, 2, 1]. Here count is the Asker guarding against duplicates, append the Adder, reverse the Mover — three families cooperating.
Exercise 4.2
Given q = ['b', 'a', 'c', 'a', 'b'], use methods to find the position of the alphabetically-first letter after sorting a copy, without disturbing q. Give both the sorted copy and the answer.
Recall Solution 4.2
We must not mutate q, so we make a copy with the function sorted:
s = sorted(q) # ['a', 'a', 'b', 'b', 'c'] (q untouched)
first = s[0] # 'a' — alphabetically first
pos = q.index(first) # where 'a' first appears in original qs = ['a', 'a', 'b', 'b', 'c'], andqis still['b', 'a', 'c', 'a', 'b'].first = 'a'.q.index('a')=1(first'a'sits at index 1 in the original).
Answers: sorted copy ['a', 'a', 'b', 'b', 'c'], position pos = 1. This is exactly the sorted() vs list.sort() distinction: sorted returns a new list and leaves the original alone.
Level 5 — Mastery
Goal: reason about edge cases, degenerate inputs, and cost.
Exercise 5.1 (empty & out-of-range edges)
For an empty list e = [], state the result of each: e.pop(), e.append(1), e.count(1), e.index(1).
Recall Solution 5.1
e.pop()→ ==IndexError== ("pop from empty list") — there is no last item to hand back.e.append(1)→ returnsNone; list becomes[1]. Appending to empty is always fine.e.count(1)→0; counting in an empty list is safe and gives zero.e.index(1)→ ==ValueError== — the value1is not present.
Pattern: the Askers/Takers that must find something (pop, index, remove) fail on absence; the counter returns 0 and the adder always works.
Exercise 5.2 (which pop is cheap?)
Using position reasoning (not timing), explain why lst.pop() is fast but lst.pop(0) is slow for a big list of length .
Recall Solution 5.2
Look again at figure s01. pop() removes the last slot — nothing else moves, so the work is a fixed amount regardless of : this is .
pop(0) removes the front slot; every remaining item at position must slide left to to close the gap. That is moves, growing with : this is .
Same logic mirrors append (fast, end) vs insert(0, x) (slow, front). See Time complexity — append O(1) vs insert O(n).
Exercise 5.3 (in-place cost of building)
You need distinct-descending like Exercise 4.1 but the input is huge and already sorted ascending: big of length . If you instead build the result by insert(0, x) at the front each loop, how many total shifts occur in the worst case (all distinct)? Give the leading term.
Recall Solution 5.3
Inserting at index 0 into a list currently of length shifts all existing items right. Doing this for gives total shifts
Leading term: — quadratic, i.e. .
Lesson: append then one reverse (as in 4.1) costs about appends () plus one reverse () = total — far cheaper than front-inserting. Choosing the right end of the list is the whole optimization.
Recall Self-test summary (cloze)
A list method that hands back a value is ==pop, count, or index==.
A list method that returns None because it mutates is ==append, insert, remove, sort, or reverse==.
remove searches by ::: value (raises ValueError if absent)
pop searches by ::: index (raises IndexError if out of range)
To sort into a new list without changing the original, use ::: sorted(lst)
Front operations cost ::: because every later item shifts
Connections
- List methods — parent topic
- Lists — creation and indexing
- Mutability vs Immutability in Python
- sorted() vs list.sort()
- Tuples — why no append/sort
- Time complexity — append O(1) vs insert O(n)
- Strings — index and count methods
- Exceptions — ValueError and IndexError