Four sessions in, every program we've written has dealt with one piece of data at a time - one name, one score, one total. Today that changes. Python is about to learn how to hold many things at once, in four different ways, each built for a specific job.
I open with a question: "if you had to store the names of every passenger on a matatu, would you create 14 separate variables?" The room always laughs, because obviously not - but that's genuinely how people try to solve this before they meet lists.
Same routine as always: open session5.py, keep it beside this article, type everything yourself.
Lists - One Variable, Many Values
A list is an ordered collection holding many values inside a single variable - think of a matatu passenger manifest, or the shopping list pinned to the kiosk wall. Every item sits at a position, called an index, and Python starts counting those positions at 0, not 1.
shopping_list = ['Bread', 'Milk', 'Sugar', 'Rice']
print(shopping_list)
print(len(shopping_list))
['Bread', 'Milk', 'Sugar', 'Rice']
4
Indexing starts at 0 because Python counts position as distance from the start, not a place number - the first item is 0 steps away, the second is 1 step away. It trips up everyone at first and becomes automatic within a week.
shopping_list = ['Bread', 'Milk', 'Sugar', 'Rice']
print(shopping_list[0]) # Bread - first item
print(shopping_list[2]) # Sugar - third item
print(shopping_list[-1]) # Rice - last item, negative counts from the end
print(shopping_list[-2]) # Sugar - second to last
Bread
Sugar
Rice
Sugar
Negative indexing is genuinely useful, not just a party trick - [-1] for "the last one" saves you from ever needing to know exactly how long a list is.
Slicing - Cutting Out a Section
Where indexing grabs one item, slicing grabs a whole section - like cutting a few slices out of a loaf, not the whole thing. The syntax is list[start:stop], and just like range(), it stops before the stop position.
numbers = [10, 20, 30, 40, 50, 60, 70]
print(numbers[1:4]) # [20, 30, 40] - position 1 up to (not including) 4
print(numbers[:3]) # [10, 20, 30] - from the start up to position 3
print(numbers[4:]) # [50, 60, 70] - from position 4 to the end
[20, 30, 40]
[10, 20, 30]
[50, 60, 70]
Add a third number and you get a step - how many positions to jump each time:
numbers = [10, 20, 30, 40, 50, 60, 70]
print(numbers[::2]) # every 2nd item
print(numbers[::-1]) # the whole list, reversed
[10, 30, 50, 70]
[70, 60, 50, 40, 30, 20, 10]
[::-1] is the fastest way to reverse anything in Python - no loop needed, just an empty start, empty stop, and a step of -1.
List Methods - Add, Remove, Sort
A method is an action performed on a list, written as list_name.method_name() - you're giving the list an instruction: add this, remove that, sort yourself.
queue = ['Amina', 'Brian']
queue.append('Njeri') # adds to the END
print(queue)
queue.insert(1, 'Otieno') # inserts AT position 1
print(queue)
['Amina', 'Brian', 'Njeri']
['Amina', 'Otieno', 'Brian', 'Njeri']
Taking things out works two ways - by value, or by position:
queue = ['Amina', 'Otieno', 'Brian', 'Njeri']
queue.remove('Otieno') # removes BY VALUE
print(queue)
served = queue.pop(0) # removes BY POSITION, and hands it back
print(f"Served: {served}")
print(queue)
['Amina', 'Brian', 'Njeri']
Served: Amina
['Brian', 'Njeri']
.pop() is the one worth remembering - it removes the item and returns it in one move, which is exactly what you want when you're processing a queue one person at a time. Sorting and checking membership round out the everyday toolkit:
scores = [67, 45, 90, 38, 72]
scores.sort()
print(scores) # [38, 45, 67, 72, 90]
fruits = ['Mango', 'Banana', 'Mango', 'Orange']
print('Mango' in fruits) # True
print(fruits.count('Mango')) # 2
[38, 45, 67, 72, 90]
True
2
Tuples - Lists That Refuse to Change
A tuple looks like a list but uses round brackets, and once it's created, it's locked - no append, no remove, no editing, ever. Use a tuple for data that genuinely should never change mid-program: a national ID number, GPS coordinates, a date of birth.
student = ('Njeri', 19, 'Kisumu')
print(student[0]) # Njeri
print(student[1]) # 19
Njeri
19
Try to change one, and Python stops you - on purpose:
student = ('Njeri', 19, 'Kisumu')
student[1] = 20 # TypeError: 'tuple' object does not support item assignment
TypeError: 'tuple' object does not support item assignment
That error isn't a bug to work around - it's the entire point of choosing a tuple in the first place. What tuples are genuinely great for is unpacking - splitting one tuple straight into several named variables in a single line:
student = ('Njeri', 19, 'Kisumu')
name, age, city = student
print(f"{name} is {age} years old, from {city}")
Njeri is 19 years old, from Kisumu
Dictionaries - Look Things Up By Name, Not Position
A dictionary stores data as key: value pairs - like a phone contact list. You don't find a contact by "the 7th entry," you find them by name.
contacts = {'Amina': '0712345678', 'Brian': '0798765432'}
print(contacts['Amina'])
print(contacts.get('Brian'))
0712345678
0798765432
Two ways to look something up, and the difference matters: contacts['Otieno'] crashes your whole program with a KeyError if that key doesn't exist. contacts.get('Otieno') just quietly returns None instead - and you can even give it a fallback: contacts.get('Otieno', 'Not found'). Use .get() whenever you're not 100% sure the key is there.
contacts = {'Amina': '0712345678'}
contacts['Njeri'] = '0700111222' # adds a new key
contacts['Amina'] = '0711000000' # updates an existing key
print(contacts)
{'Amina': '0711000000', 'Njeri': '0700111222'}
Looping through a dictionary properly means getting both the key and value at once with .items():
prices = {'Bread': 65, 'Milk': 120, 'Sugar': 150}
for item, price in prices.items():
print(f"{item}: KES {price}")
Bread: KES 65
Milk: KES 120
Sugar: KES 150
Sets - Only the Unique Ones
A set holds items with no duplicates and no guaranteed order - like a list of unique matatu routes operating in Nairobi. It doesn't matter how many matatus run Route 46, the route itself only appears once.
routes = {'Route 46', 'Route 34', 'Route 46', 'Route 11'}
print(routes)
print(len(routes))
{'Route 46', 'Route 34', 'Route 11'}
3
The duplicate 'Route 46' simply vanished - sets do that automatically, without you asking. This makes sets the fastest way to de-duplicate an existing list:
attendance = ['Amina', 'Brian', 'Amina', 'Njeri', 'Brian']
unique_students = set(attendance)
print(unique_students)
print(f"Unique students present: {len(unique_students)}")
{'Amina', 'Brian', 'Njeri'}
Unique students present: 3
Sets also come with their own comparison operators, which read almost like plain English once you know what they mean:
sacco_a = {'Route 46', 'Route 34', 'Route 11'}
sacco_b = {'Route 34', 'Route 58'}
print(sacco_a | sacco_b) # union - all routes from both
print(sacco_a & sacco_b) # intersection - routes BOTH saccos share
print(sacco_a - sacco_b) # difference - routes ONLY sacco_a has
{'Route 46', 'Route 34', 'Route 11', 'Route 58'}
{'Route 34'}
{'Route 46', 'Route 11'}
& for "what do both have in common" comes up constantly once you start comparing two groups of anything - which students take two particular subjects, which customers bought from two different campaigns, and so on.
Putting It Together - Kiosk Stock Tracker
Every structure from today shows up in one program: a dictionary for stock and prices, a list to track today's sales, and a set to capture which items sold at all.
stock = {'Bread': 20, 'Milk': 15, 'Sugar': 10, 'Rice': 8}
prices = {'Bread': 65, 'Milk': 120, 'Sugar': 150, 'Rice': 180}
sold_today = [10, 7, 5, 4] # units sold, matching stock order
items_sold = set()
total_revenue = 0
print("--- Sales Report ---")
for (item, price), units in zip(prices.items(), sold_today):
revenue = price * units
total_revenue += revenue
items_sold.add(item)
print(f"{item}: sold {units} units → KES {revenue}")
print(f"Unique items sold: {items_sold}")
print(f"Total revenue: KES {total_revenue}")
--- Sales Report ---
Bread: sold 10 units → KES 650
Milk: sold 7 units → KES 840
Sugar: sold 5 units → KES 750
Rice: sold 4 units → KES 720
Unique items sold: {'Bread', 'Milk', 'Sugar', 'Rice'}
Total revenue: KES 2960
Four structures, four different jobs, one working program. That's really what today was about - not memorising syntax, but recognising which structure fits which problem.
Top comments (0)