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))
Output
[4.5, 12.0, 32.1]
3

len() 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])
Output
4.5
32.1

amounts[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])
Output
32.1
12.0

amounts[-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:])
Output
[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)
Output
[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)
Output
[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)
Output
[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)
Output
[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)
Output
[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))
Output
48.6
4.5
32.1
3

Chapter 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))
Output
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}")
Output
0: 4.5
1: 12.0
2: 32.1

i, 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}")
Output
1. 4.5
2. 12.0
3. 32.1

start=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)
Output
[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)
Output
[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])
Output
4.5
coffee

Indexing 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.

  1. Given amounts = [4.50, 12.00, 32.10, 8.75, 45.00], print the first two and the last two using slices.
  2. Start from amounts = [12.00, 32.10], add 4.50 to the end and 8.75 to the very front, then print the result.
  3. 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".
  4. 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:])
Output
[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)
Output
[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}")
Output
1: 4.5
2: 12.0
3: 32.1

4.

amounts = [4.50, 12.00, 32.10, 8.75, 45.00]
print(sum(amounts))
print(min(amounts))
print(max(amounts))
Output
102.35
4.5
45.0

Where 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.