Recall the build pattern from lesson 5-3, where a string accumulator began as result = "". That line's job was to create the empty starting value the loop would add pieces onto.
The accumulator has to exist before the first pass, since result = result + ch reads the variable as well as writing it, and it has to start empty so that nothing unwanted survives into the result.
This unit introduces a far better container to build into. A list serves the same role, with [] as its empty starting value, and unlike a string it can be extended in place rather than rebuilt on every pass.
A list holds many values in order
One variable holds one value, but real data comes in bundles: a class's scores, the rows a database query returned, every message in a chat. The list is Python's workhorse container for ordered bundles, and after this unit it will appear in nearly every program you write.
A list is a sequence of values in square brackets, separated by commas:
scores = [85, 92, 78] names = ["Ada", "Grace"] empty = []
Everything you learned about string positions in lesson 2-1 transfers directly: len(scores) is 3, scores[0] is the first item, scores[-1] the last, and scores[0:2] slices out a new list.
The big difference from strings: lists are mutable, you can change them in place.
scores[1] = 95 # replace the item at index 1
A string method handed you a new string and left the original alone. A list assignment like this genuinely edits the list.
Familiar indexing, plus something new
The first three lines behave just as they did on strings. Line five is where lists differ.
scores = [85, 92, 78] print(len(scores)) print(scores[0]) print(scores[-1]) scores[1] = 95 print(scores)
Output
3 85 78 [85, 95, 78]
len reports three items, scores[0] reaches the first, and scores[-1] counts back to the last, exactly the numbering from lesson 2-1. Then scores[1] = 95 replaces the middle item, and the final print proves the change stuck. No new list was created and nothing needed reassigning, which is what mutability means in practice. The same line written against a string would fail outright.
[10, 20, 30, 40][1:3] evaluates to [20, 30].
Slicing behaves on lists exactly as it did on strings in lesson 2-1. The slice starts at index 1, which holds 20, and stops before index 3, so 30 is included and 40 is not.
The result is a brand new list rather than a view onto the original, which means changing it later leaves the source list untouched. That is a useful property when a function needs a working copy of part of some data.
Index assignment is the direct way to correct a single wrong item, with no need to rebuild the list around it.
colors = ["red", "grean", "blue"] colors[1] = "green" print(colors)
Output
['red', 'green', 'blue']
The misspelled word sits at index 1, so assigning to that position overwrites it and leaves the neighbors alone. The list keeps its length and its order.
One detail in the output is worth noting: Python displays the strings inside a list wrapped in single quotes, even though they were written with double quotes in the source. Both forms create identical strings, and the quotes here are just how Python chooses to show them.
Negative indexes are how you reach the recent end of a list without knowing how long it is.
readings = [18, 21, 19, 25, 22, 24, 26] print(readings[-3:]) print(readings[-1])
Output
[22, 24, 26] 26
The slice readings[-3:] starts three from the end and leaves the stop empty, so it runs to the last item and produces a three-item list. Writing it this way keeps working if the week grows to a month, whereas a hardcoded readings[4:] would silently return the wrong window. The second line uses readings[-1] for the newest single value, and note the difference in what comes back: a slice yields a list, while a plain index yields the item itself.