Lists
Intermediate · 20 min read · ▶ live playground · ✦ checkpoint
One player name fits in one variable. A whole scoreboard needs a list: one name that can hold many values, keep them in order, and still let you reach any single item when you need it.
A list keeps values in order
A list is an ordered, mutable collection of values. Ordered means the items keep their positions, mutable means the list can change in place, and a collection is one object that holds many values.
You create a list with square brackets. Think of it as a row of numbered slots. The row has one name, and each slot has a position.
players = ["Mina", "Ravi", "Noor", "Ira"]
print(players)
print(players[0])
print(players[-1])
print(players[1:3])
print(players[:2])['Mina', 'Ravi', 'Noor', 'Ira']MinaIra['Ravi', 'Noor']['Mina', 'Ravi']
The bracket rules match the string rules from lesson 3.2. An index is a numbered position. The first item is at index 0, not 1. A negative index counts backward from the end, so players[-1] means the last player.
A slice takes a smaller list from a list. players[1:3] starts at index 1 and stops before index 3, so it returns ["Ravi", "Noor"]. Same ribbon-cutting idea, new kind of value.
Lists can hold strings, numbers, Booleans, and more. In everyday code, keep one list focused on one meaning: player names in one list, scores in another, moods in another. Python allows a messy mixed list, but your future self has to read it.
Lists can grow and shrink
Because lists are mutable, list methods can change the existing list object. The name tag stays on the same list while the row of slots grows, shrinks, or reorders.
snacks = ["apples", "crackers"]
snacks.append("mango")
snacks.insert(1, "dates")
snacks.extend(["tea", "nuts"])
packed_last = snacks.pop()
snacks.remove("crackers")
print(snacks)
print("packed last:", packed_last)['apples', 'dates', 'mango', 'tea']packed last: nuts
Read the method names as small commands:
append(value)adds one value to the end.insert(index, value)adds a value before that index.extend(other_list)takes each item from another list and adds it to the end.remove(value)removes the first matching value.pop()removes and returns the last item;pop(index)removes and returns a specific item.
remove() searches by value. pop() removes by position. That difference matters when the same value appears more than once.
Try it - edit a tiny task list
Run this once, then change which item gets popped. Watch the list change in place.
Methods that answer and reorder
Some list methods answer questions. Others rearrange the list you already have.
scores = [18, 10, 18, 14]
print(scores.count(18))
print(scores.index(14))
scores.sort()
print(scores)
scores.reverse()
print(scores)23[10, 14, 18, 18][18, 18, 14, 10]
count(value) tells you how many times a value appears. index(value) tells you the first position where it appears. If the value is missing, index() raises ValueError: list.index(x): x not in list for a call like scores.index(99). A ValueError means the type is okay, but this particular value is not acceptable here; errors and exceptions get their full treatment in lesson 9.3.
sort() puts the list in order. reverse() flips the current order. Both change the list in place, so after scores.sort(), the name scores still points at the same list object, but that object now holds its items in a new order.
The aliasing trap
Aliasing means two names refer to the same object. You already know the name-tag model from lesson 2.1. With immutable values like strings, aliases rarely surprise you. With mutable lists, they can.
travel_bag = ["socks", "charger"]
spare_bag = travel_bag
travel_bag.append("snacks")
print("travel_bag:", travel_bag)
print("spare_bag:", spare_bag)travel_bag: ['socks', 'charger', 'snacks']spare_bag: ['socks', 'charger', 'snacks']
A copy is a new list object with the current items from another list. Use .copy() or a full slice [:] when you want a separate list.
original_scores = [10, 8, 9]
copied_scores = original_scores.copy()
slice_copy = original_scores[:]
original_scores.append(7)
print(original_scores)
print(copied_scores)
print(slice_copy)[10, 8, 9, 7][10, 8, 9][10, 8, 9]
Now the two backup lists stay unchanged because they are separate list objects. The items were copied at that moment; later changes to original_scores do not move those other name tags. This is a shallow copy, a copy where the outer list is separate but nested list items can still be shared; a deep copy also copies those inner objects, and you will meet copy.deepcopy() in lesson 12.5.
Nested lists hold rows
A nested list is a list inside another list. This is how beginners first model 2D data, data arranged by two positions such as row and column.
seating = [
["Ari", "Mina", "Noor"],
["Ravi", "Ira", "Tao"],
]
print(seating[0])
print(seating[1][2])
seating[1][2] = "Leah"
print(seating[1])['Ari', 'Mina', 'Noor']Tao['Ravi', 'Ira', 'Leah']
Read seating[1][2] from left to right. seating[1] gets the second row. The next [2] gets the third item inside that row. Because lists are mutable, assigning to seating[1][2] changes that slot in place.
Nested lists are useful for small grids, seating charts, game boards, and tables where every row has the same shape. For bigger labeled records, dictionaries are collections that find values by labels instead of positions; they come in lesson 6.1, so keep nested lists tidy and small for now.
Checkpoint
Answer all three to mark this lesson complete
Lists give you flexible, ordered collections that can change as your program runs. Next, tuples show the other side of the trade: fixed collections for values that should travel together without being edited.