Lesson 6 of 16 · Level 2: Intermediate

Python Lists, Tuples, Dictionaries and Sets

  • ⏱ 13 min read
  • ❓ 4 review questions
  • ✍️ Exercise with solution
In this lesson
  1. Lists: ordered and changeable
  2. Tuples: lists that don't change
  3. Dictionaries: named data
  4. Lists of dictionaries: your first "database"
  5. Sets: unique items
  6. Which one should you use?
  7. Real case: a corner store's inventory
  8. Common mistakes with collections
  9. Summary
What you'll learn
  • Work with lists and their most useful methods.
  • Use tuples and unpacking.
  • Store named data in dictionaries.
  • Choose the right structure for the job, including sets.
  • Build a corner store's inventory as a list of dictionaries and calculate what it's worth.

So far each variable has held a single value. In real life you work with collections: a customer list, the items in an order, a class roster. Python has four built-in data structures for that: lists, tuples, dictionaries and sets. Picking the right one makes your code simpler and faster, and it's one of the first things interviewers check in a junior Python developer.

4data structures come built into Python: list, tuple, dictionary and set
0duplicates allowed in a set: it's the fastest way to remove repeats
1list of dictionaries is all you need to represent a table, like a spreadsheet

Lists: ordered and changeable

products = ["keyboard", "mouse", "monitor"]
prices = [25.0, 12.5, 180.0]
mixed = ["Jane", 29, True]   # types can be mixed, though it's unusual
empty = []

Reading and changing items

print(products[0])     # keyboard
print(products[-1])    # monitor
print(products[0:2])   # ['keyboard', 'mouse']
products[1] = "wireless mouse"
print(len(products))   # 3
print("monitor" in products)  # True

Indexes and slices work exactly like they did with strings in lesson 3. The big difference: you can change a list in place, which you can't do with a string.

The methods you'll use most

MethodWhat it does
items.append(x)Adds x to the end
items.insert(i, x)Inserts x at position i
items.remove(x)Removes the first occurrence of x
items.pop() / items.pop(i)Removes and returns the last item / the item at position i
items.sort()Sorts the list in place
sorted(items)Returns a sorted copy (the original stays as it was)
items.reverse()Reverses the order
items.count(x) / items.index(x)How many times x appears / where it is
scores = [78, 92, 65, 88]
scores.append(95)
scores.sort(reverse=True)
print(scores)                     # [95, 92, 88, 78, 65]
print(sum(scores) / len(scores))  # 83.6
Assigning a list doesn't copy it

b = a doesn't create a copy: a and b are the same list with two names, so changing one changes the other. To copy, use b = a.copy() or b = list(a).

1. With fruits = ["pear", "grape"], what does the list look like after fruits.append("kiwi")?

  • ["kiwi", "pear", "grape"]
  • It raises an error; lists can't change
  • ["pear", "grape", "kiwi"]

append adds the item to the end. Lists are changeable (mutable); tuples aren't.

Tuples: lists that don't change

A tuple is like a list, but immutable: once created, it can't be modified. You write it with parentheses:

coordinates = (41.8781, -87.6298)   # downtown Chicago
date = (2026, 10, 31)
latitude, longitude = coordinates  # unpacking
print(latitude)                    # 41.8781

Use tuples for values that belong together and shouldn't change (coordinates, dates, fixed records), and as dictionary keys. Unpacking, assigning a tuple's values to several variables at once, is something you'll see everywhere, including in the for day, sale in zip(...) loop from lesson 5.

Dictionaries: named data

A dictionary stores key: value pairs. Instead of reaching an item by position, you reach it by name. It's the most useful structure for representing real-world "things":

customer = {
    "name": "Jane Doe",
    "email": "jane@mail.com",
    "city": "Phoenix",
    "purchases": 12,
}

print(customer["name"])            # Jane Doe
customer["purchases"] += 1         # update a value
customer["phone"] = "602-555-0147" # add a new key
del customer["city"]               # delete a key

Safe access with get()

print(customer["address"])            # KeyError: the key doesn't exist
print(customer.get("address"))        # None
print(customer.get("address", "—"))   # — (a default value)

Looping over a dictionary

stock = {"keyboard": 15, "mouse": 40, "monitor": 3}

for product, quantity in stock.items():
    alert = "  ⚠ reorder" if quantity < 5 else ""
    print(f"{product:10} {quantity:3}{alert}")
keyboard    15
mouse       40
monitor      3  ⚠ reorder

There are also .keys() (keys only) and .values() (values only): sum(stock.values()) gives you the total units, 58.

Counting with a dictionary

A classic use: counting how many times each item appears.

sales = ["mouse", "keyboard", "mouse", "monitor", "mouse"]
counts = {}
for product in sales:
    counts[product] = counts.get(product, 0) + 1
print(counts)   # {'mouse': 3, 'keyboard': 1, 'monitor': 1}

The trick is counts.get(product, 0): the first time a product shows up there's no key yet, so get returns 0 instead of crashing.

2. With prices = {"coffee": 12}, what does prices.get("tea", 0) return?

  • KeyError
  • 0
  • 12

.get() returns the default value when the key doesn't exist; prices["tea"] would raise a KeyError.

Lists of dictionaries: your first "database"

Combine the two structures and you can represent a table, like a spreadsheet: each dictionary is a row and each key is a column.

orders = [
    {"id": 1, "customer": "Jane", "total": 120.0, "paid": True},
    {"id": 2, "customer": "Mike", "total": 85.5,  "paid": False},
    {"id": 3, "customer": "Jane", "total": 240.0, "paid": True},
]

unpaid = [o for o in orders if not o["paid"]]
jane_total = sum(o["total"] for o in orders if o["customer"] == "Jane")
biggest = max(orders, key=lambda o: o["total"])

print(len(unpaid))     # 1
print(jane_total)      # 360.0
print(biggest["id"])   # 3

This is essentially how data arrives from an API or a JSON file, as you'll see in lessons 8 and 12. The key=lambda o: o["total"] argument tells max() (and sorted()) which field to compare.

Sets: unique items

emails = ["a@x.com", "b@x.com", "a@x.com"]
unique = set(emails)
print(len(unique))   # 2

team_a = {"Jane", "Mike", "Eva"}
team_b = {"Eva", "Carlos"}
print(team_a & team_b)   # {'Eva'}            (intersection)
print(team_a | team_b)   # all 4 names        (union)
print(team_a - team_b)   # {'Jane', 'Mike'}   (difference)

Sets have no order and no duplicates. They're ideal for removing repeats from an email list and for checking membership very quickly. Because they have no order, the names may print in a different order on your computer.

3. What does len(set([1, 2, 2, 3, 3, 3])) return?

  • 3
  • 6
  • 2

A set keeps only unique items: {1, 2, 3}.

Which one should you use?

List []Ordered and changeable
Access
By position: products[0]
Use it for
A sequence you'll modify: today's orders, test scores, prices.
Most common
Tuple ()Ordered and immutable
Access
By position or unpacking: lat, lon = coordinates
Use it for
A fixed group of values that shouldn't change: dates, coordinates.
Fixed data
Dictionary {key: value}Named pairs
Access
By key: customer["email"] or .get()
Use it for
Looking things up by name or ID: customer records, stock by product.
Lookup by name
Set set()No order, no duplicates
Access
You only ask if it's there: "Eva" in team
Use it for
Unique values and set math: deduplicated emails, customers in common.
Unique values

Real case: a corner store's inventory

Fishtown Corner Market in Philadelphia wants to know how much cash is tied up in inventory by category and which products need reordering. Each product is a dictionary; the full inventory is a list. This example uses all four structures from the lesson:

# Inventory at Fishtown Corner Market (Philadelphia) - wholesale cost in USD
inventory = [
    {"product": "Rice 5 lb",           "category": "Pantry",    "stock": 40, "cost": 3.10},
    {"product": "Peanut butter 16 oz", "category": "Pantry",    "stock": 6,  "cost": 2.45},
    {"product": "Milk 1 gal",          "category": "Dairy",     "stock": 30, "cost": 2.60},
    {"product": "Cheddar 8 oz",        "category": "Dairy",     "stock": 4,  "cost": 2.20},
    {"product": "Detergent 92 oz",     "category": "Cleaning",  "stock": 18, "cost": 8.40},
    {"product": "Soda 2 L",            "category": "Beverages", "stock": 25, "cost": 1.35},
]
MIN_STOCK = 8

value_by_category = {}
for item in inventory:
    value = item["stock"] * item["cost"]
    cat = item["category"]
    value_by_category[cat] = value_by_category.get(cat, 0) + value

to_reorder = [i["product"] for i in inventory if i["stock"] < MIN_STOCK]
categories = {i["category"] for i in inventory}   # set: no duplicates

ranking = sorted(value_by_category.items(), key=lambda pair: pair[1], reverse=True)
for cat, value in ranking:
    print(f"{cat:10} ${value:7.2f}")
print(f"Total value: ${sum(value_by_category.values()):.2f}")
print("Reorder:", ", ".join(to_reorder))
print("Number of categories:", len(categories))

Output:

Cleaning   $ 151.20
Pantry     $ 138.70
Dairy      $  86.80
Beverages  $  33.75
Total value: $410.45
Reorder: Peanut butter 16 oz, Cheddar 8 oz
Number of categories: 4
Inventory value by category Cleaning $151.20 (37%), pantry $138.70 (34%), dairy $86.80 (21%) and beverages $33.75 (8%), out of $410.45 in total. $410.45 in inventory
  • Cleaning (37%) $151.20Just 18 jugs of detergent: a high unit cost ties up the most cash.
  • Pantry (34%) $138.70Rice and peanut butter; peanut butter needs reordering.
  • Dairy (21%) $86.80Milk and cheese: fast sellers that expire quickly.
  • Beverages (8%) $33.75
Value = stock × unit cost, summed by category with the d[key] = d.get(key, 0) + value pattern.

Notice which structure does each job: the list keeps the products in order; each dictionary names its fields; value_by_category is a dictionary used as an accumulator; .items() returns pairs that are tuples (which is why pair[1] is the value); and the set categories, built with braces in a comprehension, drops duplicates automatically. With 6 products you could do this by hand. With the 2,000 items a real store carries, the code is exactly the same.

4. With prices = [3, 1, 2], what does sorted_prices = prices.sort() followed by print(sorted_prices) display?

  • [1, 2, 3]
  • None
  • [3, 1, 2]

.sort() sorts the original list and returns nothing (it returns None). If you want a sorted copy, use sorted_prices = sorted(prices).

Common mistakes with collections

The number one dictionary error is asking for a key that doesn't exist, for example by typing "price" when the key is called "cost":

Traceback (most recent call last):
  File "inventory.py", line 2, in <module>
    value = item["stock"] * item["price"]
                            ~~~~^^^^^^^^^
KeyError: 'price'

The message only shows the missing key, in quotes. Check how it was spelled when you built the dictionary (capitals included: "Category" and "category" are different keys), or use .get() if the value might be missing.

items = items.sort()

You end up with None. Write just items.sort(), or use sorted(items).

Thinking b = a copies the list.

It's the same list with two names. Copy with a.copy().

Changing a tuple: date[1] = 11.

Tuples are immutable (TypeError: 'tuple' object does not support item assignment). Create a new one or use a list.

items.remove("sugar") without knowing whether it's there.

If it's missing you get ValueError: list.remove(x): x not in list. Check first with if "sugar" in items:.

Searching a whole list to find something by name.

If you look things up by an ID or name, use a dictionary: the lookup is instant.

Practice exercise: a contact book

Write a program with a menu that lets the user:

  1. Add a contact (name and phone).
  2. Search for a contact by name.
  3. List all contacts alphabetically.
  4. Quit.
See solution
contacts = {}

while True:
    option = input("\n1) Add  2) Search  3) List  4) Quit: ")
    if option == "1":
        name = input("Name: ").strip().title()
        phone = input("Phone: ").strip()
        contacts[name] = phone
        print("Saved")
    elif option == "2":
        name = input("Who are you looking for? ").strip().title()
        print(contacts.get(name, "Not found"))
    elif option == "3":
        for name in sorted(contacts):
            print(f"{name}: {contacts[name]}")
    elif option == "4":
        break
    else:
        print("Invalid option")

Because names go through .title(), searching for "ZOE KIM" finds "Zoe Kim." The catch: when you close the program, the contacts disappear. In the next lesson you'll organize this code into functions, and in lesson 8 you'll save it to a file.

Summary

  • Lists are ordered and changeable (append, remove, sort); sorted() returns a copy.
  • Tuples are immutable, perfect for fixed data and unpacking.
  • Dictionaries store key-value pairs; use .get() for safe access and .items() to loop.
  • Sets hold unique items and support intersection, union and difference.
  • A list of dictionaries represents a table of data, the same shape you'll get from APIs and JSON.

Next lesson: Python Functions: Parameters, return and Modules. You'll package your code into reusable blocks with parameters, default values and return.

🎓 Save your progress and get a free certificate

Create your account in 30 seconds, mark your lessons and download your certificate at the end.

Enroll for free