DEV Community

Cover image for Teaching Python to Hold More Than One Thing
Navas Herbert
Navas Herbert

Posted on

Teaching Python to Hold More Than One Thing

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))
Enter fullscreen mode Exit fullscreen mode
['Bread', 'Milk', 'Sugar', 'Rice']
4
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
Bread
Sugar
Rice
Sugar
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
[20, 30, 40]
[10, 20, 30]
[50, 60, 70]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
[10, 30, 50, 70]
[70, 60, 50, 40, 30, 20, 10]
Enter fullscreen mode Exit fullscreen mode

[::-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)
Enter fullscreen mode Exit fullscreen mode
['Amina', 'Brian', 'Njeri']
['Amina', 'Otieno', 'Brian', 'Njeri']
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode
['Amina', 'Brian', 'Njeri']
Served: Amina
['Brian', 'Njeri']
Enter fullscreen mode Exit fullscreen mode

.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
Enter fullscreen mode Exit fullscreen mode
[38, 45, 67, 72, 90]
True
2
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
Njeri
19
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
TypeError: 'tuple' object does not support item assignment
Enter fullscreen mode Exit fullscreen mode

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}")
Enter fullscreen mode Exit fullscreen mode
Njeri is 19 years old, from Kisumu
Enter fullscreen mode Exit fullscreen mode

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'))
Enter fullscreen mode Exit fullscreen mode
0712345678
0798765432
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode
{'Amina': '0711000000', 'Njeri': '0700111222'}
Enter fullscreen mode Exit fullscreen mode

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}")
Enter fullscreen mode Exit fullscreen mode
Bread: KES 65
Milk: KES 120
Sugar: KES 150
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode
{'Route 46', 'Route 34', 'Route 11'}
3
Enter fullscreen mode Exit fullscreen mode

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)}")
Enter fullscreen mode Exit fullscreen mode
{'Amina', 'Brian', 'Njeri'}
Unique students present: 3
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
{'Route 46', 'Route 34', 'Route 11', 'Route 58'}
{'Route 34'}
{'Route 46', 'Route 11'}
Enter fullscreen mode Exit fullscreen mode

& 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}")
Enter fullscreen mode Exit fullscreen mode
--- 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
Enter fullscreen mode Exit fullscreen mode

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)