Lists
Create, change and sort Python lists, and avoid the aliasing bug that catches almost every beginner.
What you will learn
- Create lists and read, add, remove and replace items
- Choose between sort and sorted
- Explain why two names can refer to the same list
- Copy a list, and know when a copy is only shallow
On this page
Up to now every variable has held one value. Real programs hold many: the items in a basket, the lines in a file, the scores in a class. The list is Python’s everyday answer. It is an ordered, changeable sequence of values, and you will use it constantly. The shared fundamentals lesson on arrays and lists (linked at the top of this page) explains the general idea of an indexed collection. This lesson covers how Python spells it.
Creating and reading a list
Square brackets make a list. Indexes start at 0, and negative indexes count from the end, so tasks[-1] is the last item.
tasks = ["write", "test", "ship"]print(tasks)print(tasks[0], tasks[-1])print(len(tasks))print("ship" in tasks, "nap" in tasks)['write', 'test', 'ship']write ship3True FalseAssigning to an index replaces that item, which is the key difference from strings and tuples: a list is mutable.
tasks = ["write", "test", "ship"]tasks[0] = "draft"print(tasks)['draft', 'test', 'ship']Reading past the end is an error. Check len(items) first, or use an index you know is there:
tasks = []print(tasks[0])IndexError: list index out of rangeYou already know the loops that go with lists. for task in tasks: visits each item, and enumerate and reversed from the loops lesson work on lists too.
tasks = ["write", "test", "ship"]for number, task in enumerate(tasks, start=1): print(number, task)for task in reversed(tasks): print(task, end=" ")print()1 write2 test3 shipship test writeAdding and removing
tasks = ["write", "test"]tasks.append("ship")tasks.extend(["celebrate", "rest"])tasks.insert(0, "plan")print(tasks)['plan', 'write', 'test', 'ship', 'celebrate', 'rest']append(x)adds one item at the end.extend(iterable)adds every item from another iterable.insert(i, x)puts an item at positioniand shifts the rest along.
tasks = ["plan", "write", "test", "ship", "celebrate", "rest"]last = tasks.pop()first = tasks.pop(0)print(last, first, tasks)
tasks.remove("test")print(tasks, len(tasks))print(tasks.index("ship"))rest plan ['write', 'test', 'ship', 'celebrate']['write', 'ship', 'celebrate'] 31pop()removes and returns the last item.pop(i)does the same for positioni.remove(x)deletes the first item equal tox.index(x)finds the position of the first match.x in itemsjust asks whether it is there.items.clear()empties the list.
remove and index raise ValueError when the item is missing, so test with in first:
tasks = ["write"]tasks.remove("nap")ValueError: list.remove(x): x not in listNotice that these methods change the list in place and mostly return None, with pop as the exception. Writing tasks = tasks.append("x") replaces your list with None.
Lists can also be joined with + and repeated with *, which build new lists:
print([1, 2] + [3])print([1, 2] * 2)[1, 2, 3][1, 2, 1, 2]Summing up a list
The built-in functions len, min, max and sum accept a whole list:
scores = [88, 95, 70, 91]print(len(scores), min(scores), max(scores), sum(scores))print(sum(scores) / len(scores))4 70 95 34486.0sort versus sorted
sorted(items) builds a new sorted list and leaves the original alone. items.sort() sorts in place and returns None. Pick sorted when you still need the original order, and sort when you do not.
scores = [88, 95, 70, 91]ranked = sorted(scores, reverse=True)print(ranked, scores)
result = scores.sort()print(result, scores)[95, 91, 88, 70] [88, 95, 70, 91]None [70, 88, 91, 95]Both accept reverse=True and a key= function that says what to sort by: key=len orders words by length. The sort is stable, so items with equal keys keep their original relative order (pear stays ahead of kiwi).
words = ["pear", "fig", "banana", "kiwi"]words.sort(key=len)print(words)['fig', 'pear', 'kiwi', 'banana']Aliasing and copies
b = a does not copy a list. It gives the same list a second name, so a change through b shows up through a. The expression a is b confirms that both names point at one object.
a = [1, 2, 3]b = ab.append(4)print(a, b, a is b)[1, 2, 3, 4] [1, 2, 3, 4] TrueTo get an independent list, call a.copy() or write a[:], which the slicing lesson covers.
a = [1, 2, 3, 4]c = a.copy()c.append(5)print(a, c, a is c)[1, 2, 3, 4] [1, 2, 3, 4, 5] FalseThose copies are shallow. They copy the outer list but share the inner objects. Change shallow[0][0] and the original grid changes too, because both outer lists hold the same inner list at position 0.
grid = [[0, 0], [0, 0]]shallow = grid.copy()shallow[0][0] = 9print(grid)[[9, 0], [0, 0]]The same sharing explains the classic [[0] * 2] * 2 mistake: the outer * repeats a reference to one inner list. Build a grid with a loop, and every row is its own list:
wrong = [[0] * 2] * 2wrong[0][0] = 9print(wrong)
right = []for _ in range(2): right.append([0] * 2)right[0][0] = 9print(right)[[9, 0], [9, 0]][[9, 0], [0, 0]]The name _ is a convention for “a loop variable I do not use”. The shared fundamentals track covers shallow versus deep copies in its memory and references lesson.
Common pitfalls
- Assigning the result of an in-place method.
items = items.sort()leavesitemsasNone. Callitems.sort()on its own line, or usesorted. - Thinking
b = acopies. It adds a name. Usea.copy()when you need a second list. - Repeating a list of lists with
*.[[0] * 2] * 2makes two references to one row. Build the rows in a loop. - Removing items while looping. Deleting from a list you are iterating over skips elements. Loop over a copy, or build a new list.
IndexErroron empty or short lists.[][0]and[1, 2][5]both raise it. Checkif items:orlen(items)first.- Using
removeon a missing item. It raisesValueError. Test withinfirst.
This program should print the words in alphabetical order, but it prints None. Fix it in a single line.
Input: none. Expected output: ['fig', 'kiwi', 'pear'].
words = ["pear", "fig", "kiwi"]words = words.sort()print(words)NoneShow solution
words = ["pear", "fig", "kiwi"]words.sort()print(words)['fig', 'kiwi', 'pear']sort() changes the list in place and returns None, so assigning its result threw the list away. Calling it on its own line keeps the sorted list. words = sorted(words) also works.
Start with the list ["buy milk", "call Ada", "water plants"]. Print it as a numbered list starting at 1. Then try to remove "call Ada" and print removed: True if it was there and removed: False if it was not, and do the same for "fly to the moon". Print the numbered list again. Finally clear the list, and print Nothing to do. if it is empty.
Input: none. Expected output:
1. buy milk2. call Ada3. water plantsremoved: Trueremoved: False1. buy milk2. water plantsNothing to do.Show solution
todos = ["buy milk", "call Ada", "water plants"]
for number, item in enumerate(todos, start=1): print(f"{number}. {item}")
for name in ["call Ada", "fly to the moon"]: if name in todos: todos.remove(name) print("removed: True") else: print("removed: False")
for number, item in enumerate(todos, start=1): print(f"{number}. {item}")
todos.clear()if not todos: print("Nothing to do.")1. buy milk2. call Ada3. water plantsremoved: Trueremoved: False1. buy milk2. water plantsNothing to do.Testing with in before remove avoids the ValueError. enumerate(..., start=1) gives people-friendly numbering, and an empty list is falsy, so not todos is the test for “nothing left”.
Given scores = [72, 95, 88, 64, 95, 81], print the three highest scores, highest first, and then print the original list to show it has not changed. Do not modify scores.
Input: none. Expected output: [95, 95, 88] then [72, 95, 88, 64, 95, 81].
Show solution
scores = [72, 95, 88, 64, 95, 81]print(sorted(scores, reverse=True)[:3])print(scores)[95, 95, 88][72, 95, 88, 64, 95, 81]sorted returns a new list, so scores is never modified. The slice [:3] takes the first three items and stops quietly at the end of a shorter list instead of raising an error.
Summary
- Lists are ordered and mutable.
append,extend,insert,popandremovechange them in place. sorted(x)returns a new list.x.sort()changesxand returnsNone.b = ashares one list.copy()makes a shallow copy, and nested lists are still shared.
Next: tuples and unpacking.