Lists
A shopping list: items in order, and you can add to it, cross things off, or rearrange it whenever you like.
Imagine keeping track of quiz scores for a class of 30 students. With what you know so far, you'd need 30 variables: score1, score2, all the way to score30. Adding them up would mean typing all 30 names, and if a new student joined, you'd have to change your code.
A list solves this. It stores many values, in order, under a single name. It's Python's most-used way to keep a collection of things.
A shopping list
Think of a shopping list on your phone. It keeps items in the order you added them. You can add something at the end, squeeze something in at the top, cross an item off, or sort the whole thing. It's always exactly as long as it needs to be.
A Python list works the same way, and like the characters in a string from Working with Strings, every item has a numbered position, its index, starting from 0.
Making a list
Write the items between square brackets, separated by commas:
scores = [88, 94, 72, 65, 91]
names = ["Maria", "Ben", "Carlo"]
empty = []
print(scores)
print(len(names))[88, 94, 72, 65, 91]
3len works on lists just like on strings: it gives the number of items. A list can hold any type of value, and even a mix, but a list where every item is the same kind of thing is much easier to work with.
Reading items
Indexing and slicing work exactly the way they do for strings:
scores = [88, 94, 72, 65, 91]
print(scores[0])
print(scores[-1])
print(scores[1:3])88
91
[94, 72]scores[0] is the first item, scores[-1] the last, and a slice gives back a new, shorter list. Ask for an index that doesn't exist, and Python stops you:
scores = [88, 94, 72]
print(scores[3]) # error: IndexError: list index out of rangeLists can change
Here's the big difference from strings. Strings can never be changed, but lists can. You can replace an item by assigning to its index:
scores = [88, 94, 72]
scores[2] = 75
print(scores)[88, 94, 75]And lists have methods for adding and removing items. Unlike string methods, these change the list in place:
| Method | What it does |
|---|---|
append(x) | add x to the end |
insert(i, x) | put x at index i, shifting the rest along |
remove(x) | remove the first item equal to x |
pop() | remove the last item, and give it back |
pop(i) | remove the item at index i, and give it back |
sort() | put the items in order |
reverse() | flip the order |
names = ["Maria", "Ben"]
names.append("Carlo")
names.insert(0, "Ana")
print(names)
names.remove("Ben")
last = names.pop()
print(names, "and", last, "was popped")['Ana', 'Maria', 'Ben', 'Carlo']
['Ana', 'Maria'] and Carlo was poppedBecause these methods change the list itself, they give back None, not a new list. So names = names.append("Eli") is a classic bug: it replaces your whole list with None. Just write names.append("Eli").
Is it in there?
The in operator checks whether a list contains an item:
names = ["Ana", "Maria", "Ben"]
print("Ben" in names)
print("Eli" not in names)True
TrueAnd index tells you where it is: names.index("Maria") is 1.
Looping through a list
Lists and for loops were made for each other:
scores = [88, 94, 72, 65, 91]
for score in scores:
print("Score:", score)Score: 88
Score: 94
Score: 72
Score: 65
Score: 91When you want each item and its position, use enumerate. It hands you both on every iteration:
names = ["Maria", "Ben", "Carlo"]
for position, name in enumerate(names, start=1):
print(f"{position}. {name}")1. Maria
2. Ben
3. Carlostart=1 makes the numbering begin at 1 instead of 0, which is what people usually want to see. That's the neater way to get positions promised in for Loops and range().
Built-in helpers: sum, min, max, sorted
Python has built-in functions that do the most common list jobs in one step:
scores = [88, 94, 72, 65, 91]
print(sum(scores))
print(min(scores), max(scores))
print(sum(scores) / len(scores))
print(sorted(scores))
print(sorted(scores, reverse=True))
print(scores)410
65 94
82.0
[65, 72, 88, 91, 94]
[94, 91, 88, 72, 65]
[88, 94, 72, 65, 91]Notice the last line. sorted(scores) gives back a new, sorted list and leaves the original alone, while scores.sort() sorts the original list itself. Use sorted() when you want to keep the original order too.
Building a list in a loop
A very common pattern: start with an empty list, and append to it inside a loop:
scores = [88, 94, 72, 65, 91]
passing = []
for score in scores:
if score >= 75:
passing.append(score)
print(passing)[88, 94, 91]You'll learn a one-line way to write this, called a comprehension, in List and Dictionary Comprehensions.
The copying surprise
What does this print?
original = [1, 2, 3]
copy = original
copy.append(4)
print(original)[1, 2, 3, 4]Remember the name tags from Variables and Naming? copy = original doesn't copy the list. It sticks a second name tag on the same list. There's only one list, so changing it through either name changes it for both.
To make a real, separate copy, use .copy() (or a full slice, original[:]):
original = [1, 2, 3]
copy = original.copy()
copy.append(4)
print(original)
print(copy)[1, 2, 3]
[1, 2, 3, 4]The same thing happens when you pass a list to a function, as promised in Parameters and Return Values. The parameter is another name tag on your list, so a function that changes the list in place changes yours:
def add_bonus(scores):
for i in range(len(scores)):
scores[i] += 5
class_scores = [70, 80, 90]
add_bonus(class_scores)
print(class_scores)[75, 85, 95]That's sometimes exactly what you want. When it isn't, have the function build and return a new list instead.
Try it
This program manages a small class list and its scores: students enroll, one transfers out, and the program prints a ranked report. Track the lists after each line and predict the output.
Running Python here
Running Python right in the browser is coming to Syntaxia soon. For now, you can read and edit the code here. If you have Python installed on your computer (see Setting Up), save it as main.py and run it with python main.py (or python3 main.py on macOS and Linux).
Check your prediction
5 students: ['Maria', 'Ben', 'Carlo', 'Dina', 'Eli']
After Ben transferred: ['Maria', 'Carlo', 'Dina', 'Eli']
Class average: 89.5
Highest: 95 by Carlo
1. Carlo (95)
2. Maria (91)
3. Eli (88)
Roster now has 5 namesBen was removed from both lists at the same index, so every name still lines up with its score. sorted(..., reverse=True) makes a ranked copy without disturbing the original order. And backup = roster didn't copy anything: adding Fe through backup added her to roster too.
Try it yourself
- Add a student named
Giowith a score of 99. Which lines of the report change? - Change
backup = rosterto make a real copy. How many names does the roster have now? - Write a loop that prints only the students who scored below the class average.
Check your understanding
Up next
Lists can change, and that's usually what you want. But some collections should never change, like the coordinates of a point or the days of the week. For those, Python has a list's fixed cousin: Tuples.
