Chapter 5
Lists
Chapters 3 and 4 used amounts = [4.50, 12.00, 32.10] without stopping to
explain it properly. This chapter does: how to build a list, get values back
out of it by position, change it, and the handful of methods you'll use
constantly.
Making a list
A list is an ordered, changeable group of values, written in square brackets, separated by commas:
amounts = [4.50, 12.00, 32.10]
print(amounts)
print(len(amounts))
[4.5, 12.0, 32.1]
3len() works on a list the same way it works on a string, from Chapter 1: it
counts how many items are in it.
A list can hold any type, including a mix, though this book's lists mostly hold one kind of thing at a time, which keeps the code that loops over them simple.
Indexing
Get one value out by its index, its position, counting from 0:
amounts = [4.50, 12.00, 32.10]
print(amounts[0])
print(amounts[2])
4.5
32.1amounts[0] is the first item, not the "zeroth"; Python (and most
programming languages) count positions starting at 0. amounts[2] is the
third item, at index 2.
Negative indexes count from the end:
amounts = [4.50, 12.00, 32.10]
print(amounts[-1])
print(amounts[-2])
32.1
12.0amounts[-1] is always the last item, useful when you don't know exactly
how long a list is.
Indexing past the end raises an error rather than returning something useless:
amounts = [4.50, 12.00, 32.10]
print(amounts[5])
That's IndexError: list index out of range. Same spirit as ValueError
from Chapter 2: an honest error instead of a silent wrong answer.
Slicing
A slice pulls out a sub-list, start:stop, stopping before the second
number, the same rule range() follows:
amounts = [4.50, 12.00, 32.10, 8.75, 45.00]
print(amounts[1:3])
print(amounts[:2])
print(amounts[2:])
[12.0, 32.1]
[4.5, 12.0]
[32.1, 8.75, 45.0]Leaving out the start means "from the beginning"; leaving out the stop means "to the end."
Changing a list
Unlike a str, a list is mutable: you can change it in place without
making a new one.
amounts = [4.50, 12.00]
amounts.append(32.10)
print(amounts)
[4.5, 12.0, 32.1]append() adds one item to the end. insert() adds one at a specific
position:
amounts = [4.50, 32.10]
amounts.insert(1, 12.00)
print(amounts)
[4.5, 12.0, 32.1]remove() deletes the first match by value; pop() removes and returns an
item by index, defaulting to the last one:
amounts = [4.50, 12.00, 32.10]
amounts.remove(12.00)
print(amounts)
last = amounts.pop()
print(last)
print(amounts)
[4.5, 32.1]
32.1
[4.5]sort() reorders the list in place:
amounts = [32.10, 4.50, 12.00]
amounts.sort()
print(amounts)
[4.5, 12.0, 32.1]Pass reverse=True for largest first:
amounts = [32.10, 4.50, 12.00]
amounts.sort(reverse=True)
print(amounts)
[32.1, 12.0, 4.5]sum(), min(), max(), and checking membership
Four built-in functions cover the most common questions you'd ask about a list of numbers, without writing a loop yourself:
amounts = [4.50, 12.00, 32.10]
print(sum(amounts))
print(min(amounts))
print(max(amounts))
print(len(amounts))
48.6
4.5
32.1
3Chapter 7 writes a total() function that does the same job as sum() by
hand, on purpose, because writing it yourself is how functions are taught.
Once you understand how, reaching for sum() in real code is the better
choice; there's no reason to write a loop Python already gives you.
in checks whether a value appears anywhere in a list, and .count() and
.index() answer "how many times" and "at what position":
amounts = [4.50, 12.00, 32.10, 4.50]
print(12.00 in amounts)
print(amounts.count(4.50))
print(amounts.index(32.10))
True
2
2.index() returns the position of the first match; with 4.50 appearing
twice above, .count() is how you'd notice that before .index() quietly
gives you only the first one.
Looping with a position
for amount in amounts gives you each value but not its position. When you
need both, enumerate() gives you a running index alongside each value:
amounts = [4.50, 12.00, 32.10]
for i, amount in enumerate(amounts):
print(f"{i}: {amount}")
0: 4.5
1: 12.0
2: 32.1i, amount in enumerate(amounts) unpacks two values per pass: the index and
the item. You'll see this shape whenever position matters, like numbering a
printed list starting from 1:
amounts = [4.50, 12.00, 32.10]
for i, amount in enumerate(amounts, start=1):
print(f"{i}. {amount}")
1. 4.5
2. 12.0
3. 32.1start=1 shifts the count to begin at 1 instead of 0.
Names vs. the list itself
This is the one idea in this chapter worth slowing all the way down for. Chapter 2 said a variable is a name pointing at a value, not a box holding it. For a list, that distinction has a real, sometimes surprising consequence: two names can point at the same list.
original = [4.50, 12.00]
same_list = original
same_list.append(32.10)
print(original)
print(same_list)
[4.5, 12.0, 32.1]
[4.5, 12.0, 32.1]same_list = original didn't copy the list, it made a second name pointing
at the exact same one. Changing it through same_list changed what
original sees too, because there was only ever one list. If you actually
want an independent copy, ask for one explicitly:
original = [4.50, 12.00]
copy = original.copy()
copy.append(32.10)
print(original)
print(copy)
[4.5, 12.0]
[4.5, 12.0, 32.1].copy() makes a genuinely separate list, so changing one doesn't touch the
other. Keep this in mind; Chapter 7 shows exactly where this bites people the
most, in a function's default arguments.
A quick look at tuples
A tuple looks like a list but with parentheses instead of square brackets, and it can't be changed after it's created:
point = (4.50, "coffee")
print(point[0])
print(point[1])
4.5
coffeeIndexing and slicing work exactly like a list's. What's missing is
everything that changes one: no .append(), no .remove(), no item
assignment.
point = (4.50, "coffee")
point[0] = 5.00
That's TypeError: 'tuple' object does not support item assignment. Use a
tuple for a small, fixed group of values that belong together and shouldn't
change, like a coordinate pair or, as you saw in Chapter 2, what two names
on the left of an unpacking assignment are really matching against on the
right. Use a list, this chapter's main subject, when you're building up or
changing a group of values over time. This book's own expenses is
always a list for exactly that reason: it grows.
Practice
Try each of these before you read the solution under it.
- Given
amounts = [4.50, 12.00, 32.10, 8.75, 45.00], print the first two and the last two using slices. - Start from
amounts = [12.00, 32.10], add4.50to the end and8.75to the very front, then print the result. - Given
amounts = [4.50, 12.00, 32.10], print each one with its position starting from 1, one per line, formatted as"1: 4.5". - Given
amounts = [4.50, 12.00, 32.10, 8.75, 45.00], print the sum, the smallest, and the largest, one per line, using the built-ins from this chapter.
Solutions
1.
amounts = [4.50, 12.00, 32.10, 8.75, 45.00]
print(amounts[:2])
print(amounts[-2:])
[4.5, 12.0]
[8.75, 45.0]2. insert(0, ...) adds at the very front; append() adds at the end.
amounts = [12.00, 32.10]
amounts.append(4.50)
amounts.insert(0, 8.75)
print(amounts)
[8.75, 12.0, 32.1, 4.5]3.
amounts = [4.50, 12.00, 32.10]
for i, amount in enumerate(amounts, start=1):
print(f"{i}: {amount}")
1: 4.5
2: 12.0
3: 32.14.
amounts = [4.50, 12.00, 32.10, 8.75, 45.00]
print(sum(amounts))
print(min(amounts))
print(max(amounts))
102.35
4.5
45.0Where this leaves you
You can build, index, slice, and change a list, use enumerate() when you
need a position alongside each value, and you understand why two names can
point at the same list and when that matters. Chapter 6 introduces the
dictionary, and with it, the shape this book's expense entries use from here
to the end.