PYTHON / LISTS AND TUPLES
Adding and removing list items
Grow and shrink lists in place with append, extend, insert, pop, remove, del and clear, and predict which call returns a value and which raises.
What you will learn
- Use append to add one object; use extend or += to add each element of an iterable
- Remember pop returns the removed item while append, insert, remove and del return nothing
- Choose remove for a value and pop/del for a position, and expect ValueError vs IndexError
- Rebuild a new list instead of removing items while a for loop reads the same list
Understanding Adding and removing list items
A list is a mutable, growable sequence of references, so the calls that add and remove items change the existing object rather than producing a new one. That is why append, insert, extend, remove and clear all evaluate to None: their useful effect is the mutation, not a return value. The one exception is pop, which removes an item and hands it back, which is exactly what you want when a list is being used as a stack.
The adding operations differ in how many items they add. append(x) puts x at the end as a single element no matter what x is, so appending a list nests it. extend(iterable) loops over its argument and appends every element it yields, which is why extend("hi") adds two characters rather than one string; the += operator on a list is the same operation, so it also accepts any iterable. insert(i, x) puts x at index i and shifts every item from i onward one slot to the right, so inserting at the front costs work proportional to the length while appending is cheap.
The removing operations split by how you name the victim. pop() and pop(i) and del items[i] address an item by position and raise IndexError if that position does not exist; remove(value) scans from the left, deletes the first item equal to the value, and raises ValueError if no item matches. remove never deletes more than one match, and clear() empties the list while keeping the same object, so every other name pointing at that list sees the empty result.
tasks = ["wash", "dry"]
tasks.append("fold")
print(tasks)
tasks.extend(["iron", "store"])
print(tasks)
tasks.insert(1, "soak")
print(tasks)
last = tasks.pop()
first = tasks.pop(0)
print(last, first, tasks)
tasks.remove("soak")
print(tasks)
print(tasks.append("sort"), tasks)List add/remove operations mutate the list in place and return None (except pop), and they differ in whether they address an item by position or by value.
Worked examples
append adds one item, extend adds many
Shows how the same string argument produces one element with append but two with extend or +=.
a = [1, 2]
a.append("hi")
print(a)
b = [1, 2]
b.extend("hi")
print(b)
c = [1, 2]
c += "hi"
print(c)
try:
print([1, 2] + "hi")
except TypeError as e:
print("TypeError:", e)Example explained
Line 1append treats its argument as a single object, so the two-character string becomes one element.
Line 2extend iterates its argument, and iterating a string yields characters, so two items land in the list.
Line 3c += "hi" calls the same extend machinery, which is why it accepts any iterable and not just lists.
Line 4The + operator is stricter: it builds a brand new list and demands another list, so a str operand is a TypeError.
Removing by position, by value, and all at once
Contrasts del, remove, pop and clear, including the two different exceptions they raise.
stack = ["a", "b", "c"]
del stack[1]
print(stack)
nums = [4, 7, 4, 9]
nums.remove(4)
print(nums)
try:
nums.remove(5)
except ValueError as e:
print("ValueError:", e)
print(nums.pop(), nums)
nums.clear()
print(nums)
try:
nums.pop()
except IndexError as e:
print("IndexError:", e)Example explained
Line 1del stack[1] is a statement, not a method call, and it deletes by index without giving anything back.
Line 2remove(4) scans from the left and deletes only the first matching 4, so the later 4 survives.
Line 3Asking remove for a value that is absent raises ValueError, so guard it with `in` or try/except.
Line 4pop() returns the removed item, and popping an empty list raises IndexError rather than returning None.
Removing while iterating skips items
Demonstrates why deleting from a list inside a for loop over that same list misses elements.
nums = [1, 2, 2, 3, 2, 4]
for n in nums:
if n == 2:
nums.remove(n)
print("during-iteration:", nums)
nums = [1, 2, 2, 3, 2, 4]
kept = []
for n in nums:
if n != 2:
kept.append(n)
print("rebuilt:", kept)Example explained
Line 1The for loop walks the list by an internal index, and remove shifts every later item one slot down.
Line 2After the first removal the loop reads index 2, which is now 3, so the 2 that slid into index 1 is never seen.
Line 3The loop also stops early because the list got shorter, leaving a stray 2 behind.
Line 4Building a separate list with append keeps reading and writing on different objects, so nothing is skipped.
Important notes
insert clamps out-of-range indexes instead of failing: insert(99, x) appends and insert(-99, x) puts the item first, while pop and del on a bad index raise IndexError.
pop() from the end is cheap, but pop(0) and insert(0, x) shift every remaining item, so use collections.deque when you add and remove at the front repeatedly.
Common mistakes
Writing `tasks = tasks.append("x")`: append returns None, so the name is rebound to None and the list is lost, and the next tasks.append raises AttributeError.
Calling `items.extend("abc")` or `items.extend(5)` expecting one new element: the first adds three separate characters, the second raises TypeError because an int is not iterable.
Using remove(value) as if it deleted every match or as if it were safe: it deletes only the first equal item and raises ValueError when the value is absent.
Try it yourself
Change, predict, then run
Start from queue = ['ann', 'bo', 'cy']: append 'dee', insert 'zed' at index 0, remove 'bo' by value, then pop the first name into a variable and print that name plus the final list. You should print zed and ['ann', 'cy', 'dee'].
Open the Python workspaceCheck your understanding
Given row = ['a', 'b', 'c'], what does row look like after row.insert(-1, 'x')?
- ['a', 'b', 'x', 'c']
- ['a', 'b', 'c', 'x']
- ['x', 'a', 'b', 'c']
- IndexError, because insert rejects negative indexes
Show answer
insert places the new item before whatever currently sits at the given index, and index -1 is 'c', so 'x' lands between 'b' and 'c'. It is tempting to read -1 as 'the end' and expect ['a', 'b', 'c', 'x'], but only append (or insert with an index of len(row) or larger) puts an item after the last element; insert also never raises IndexError because it clamps out-of-range indexes.