Lists, Tuples, Dicts & Sets
After this lesson, you will be able to:
- Create and manipulate lists: indexing, slicing, appending, sorting, and removing elements
- Understand tuples as immutable (unchangeable) sequences and when to use them
- Build and query dictionaries for key-value data storage
- Use sets for unique collections and set operations (union, intersection, difference)
- Write list comprehensions to create lists with a single elegant expression
- Understand why adding items to a list is fast, but inserting at the front is slow, and choose the right structure for the job
This is the cheat sheet for this whole lesson. Bookmark it. Every section below zooms into one row of this table.
Before You Start
#Lists — Ordered, Mutable Collections
A list is Python's most versatile data structure. It holds an ordered collection of items that you can change (add, remove, reorder) at any time.
# Creating lists
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [42, "hello", True, 3.14, None] # lists can mix types
empty = [] # empty list
print(fruits) # ['apple', 'banana', 'cherry']
print(len(fruits)) # 3Try it! Open the Python REPL and create your own shopping list:my_list = ["milk", "eggs", "bread"]. Then trymy_list.append("butter")and print it.
#Indexing — Accessing Individual Items
colors = ["red", "green", "blue", "yellow", "purple"]
# Positive indexing (from the start)
print(colors[0]) # "red" (first item)
print(colors[1]) # "green" (second item)
print(colors[4]) # "purple" (fifth item, index 4)
# Negative indexing (from the end)
print(colors[-1]) # "purple" (last item)
print(colors[-2]) # "yellow" (second to last)
# Modify an item
colors[0] = "crimson"
print(colors) # ['crimson', 'green', 'blue', 'yellow', 'purple']HitIndexError,KeyError, orTypeError: list indices must be integers? Off-by-one on indexes, accessing a missing dict key, or using a string when an int is needed — these are the three indexing crashes. See the error decoder for fixes.
#Slicing — Extracting Sub-lists
list[start:stop:step]. Like range(), the stop value is excluded.nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[2:5]) # [2, 3, 4] (index 2 up to but not 5)
print(nums[:3]) # [0, 1, 2] (from start to index 3)
print(nums[7:]) # [7, 8, 9] (from index 7 to end)
print(nums[::2]) # [0, 2, 4, 6, 8] (every other item)
print(nums[::-1]) # [9, 8, 7, ..., 0] (reversed!)
print(nums[1:8:3]) # [1, 4, 7] (start=1, stop=8, step=3)What does [1, 2, 3][1:] return?
#Common List Methods
heroes = ["Iron Man", "Thor", "Hulk"]
# Add items
heroes.append("Black Widow") # add to end
heroes.insert(1, "Captain America") # insert at index 1
print(heroes)
# ['Iron Man', 'Captain America', 'Thor', 'Hulk', 'Black Widow']
# Remove items
heroes.remove("Hulk") # remove by value
popped = heroes.pop() # remove and return last item
print(popped) # "Black Widow"
del heroes[0] # remove by index
# Search
print("Thor" in heroes) # True
print(heroes.index("Thor")) # 1
# Sort
scores = [85, 92, 78, 95, 88]
scores.sort() # sort in place (ascending)
print(scores) # [78, 85, 88, 92, 95]
scores.sort(reverse=True) # descending
print(scores) # [95, 92, 88, 85, 78]
# Copy
original = [1, 2, 3]
copy = original.copy() # creates a new list
copy.append(4)
print(original) # [1, 2, 3] (not affected)
print(copy) # [1, 2, 3, 4]What gets printed? nums = [1, 2, 3] result = nums.append(4) print(result)
#Aliasing: when two names point to the same list
b = a, you do NOT make a copy. You make a second name for the same list.#Tuples — Immutable Sequences
# Creating tuples
coordinates = (10, 20)
rgb_red = (255, 0, 0)
single = (42,) # note the trailing comma for single-item tuples
# Accessing works the same as lists
print(coordinates[0]) # 10
print(rgb_red[-1]) # 0
# But you CANNOT modify them
# coordinates[0] = 99 # TypeError: 'tuple' object does not support item assignment#When to Use Tuples vs Lists
- Use a list when you need to add, remove, or change items (shopping list, student roster, training data)
- Use a tuple when the data should not change (coordinates, RGB colors, database records, function return values)
# Tuples are perfect for returning multiple values from a function
def get_min_max(numbers):
return (min(numbers), max(numbers))
result = get_min_max([3, 1, 4, 1, 5, 9])
print(result) # (1, 9)
# Tuple unpacking -- assign each value to a separate variable
low, high = get_min_max([3, 1, 4, 1, 5, 9])
print(f"Min: {low}, Max: {high}") # Min: 1, Max: 9You run `point = (3, 4)` then `point[0] = 99`. What happens?
#Dictionaries — Key-Value Pairs
# Creating a dictionary
student = {
"name": "Alex",
"age": 16,
"grade": "11th",
"gpa": 3.85,
"is_honor_roll": True
}
# Accessing values by key
print(student["name"]) # "Alex"
print(student["gpa"]) # 3.85
# Using .get() -- returns None instead of error for missing keys
print(student.get("email")) # None
print(student.get("email", "N/A")) # "N/A" (default value)
# Adding and modifying
student["email"] = "alex@school.edu" # add new key
student["gpa"] = 3.90 # update existing key
# Removing
del student["is_honor_roll"]
removed_value = student.pop("email") # remove and return value
print(student)#Iterating Over Dictionaries
book = {
"title": "The Hobbit",
"author": "J.R.R. Tolkien",
"pages": 310,
"published": 1937,
}
# Loop through keys
for key in book:
print(key)
# Loop through values
for value in book.values():
print(value)
# Loop through both (most common)
for key, value in book.items():
print(f"{key}: {value}")#Nested Dictionaries
Dictionaries can contain other dictionaries, creating complex data structures:
# This is exactly what an API response looks like
api_response = {
"status": "ok",
"usage": {
"requests_today": 150,
"requests_left": 500,
"limit": 650,
},
"choices": [
{"text": "Hello! How can I help?", "finish_reason": "stop"}
],
}
# Access nested values
print(api_response["usage"]["limit"]) # 650
print(api_response["choices"][0]["text"]) # "Hello! How can I help?"What happens? user = {"name": "Alex"} print(user["age"])
None like some other languages do. This is intentional: it forces you to acknowledge missing data instead of silently propagating bad values. For safe access, use .get(): user.get("age") returns None if missing, and user.get("age", 0) returns 0. This is one of the most common production crashes when parsing JSON from APIs — always assume keys might be missing.Which of these can be used as a dict KEY?
#Sets — Unique Collections
# Creating sets
fruits = {"apple", "banana", "cherry"}
numbers = {1, 2, 3, 2, 1} # duplicates are automatically removed
print(numbers) # {1, 2, 3}
# From a list (great for removing duplicates!)
words = ["the", "cat", "sat", "on", "the", "mat", "the"]
unique_words = set(words)
print(unique_words) # {'cat', 'mat', 'on', 'sat', 'the'}
print(f"Vocabulary size: {len(unique_words)}") # 5#Set Operations
Sets support mathematical operations like union, intersection, and difference:
python_devs = {"Alice", "Bob", "Charlie", "Diana"}
js_devs = {"Bob", "Diana", "Eve", "Frank"}
# Union -- everyone who knows at least one language
print(python_devs | js_devs)
# {'Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank'}
# Intersection -- people who know both
print(python_devs & js_devs)
# {'Bob', 'Diana'}
# Difference -- Python devs who do NOT know JS
print(python_devs - js_devs)
# {'Alice', 'Charlie'}
# Symmetric difference -- people who know exactly one
print(python_devs ^ js_devs)
# {'Alice', 'Charlie', 'Eve', 'Frank'}Sets are great for membership testing (is this item in the set?) because lookups are nearly instant, no matter how large the set is:
# Checking membership -- O(1) time, very fast
stop_words = {"the", "a", "an", "is", "are", "was", "were", "in", "on", "at"}
word = "the"
print(word in stop_words) # True -- instant lookupWhat does `set([1, 2, 2, 3, 3, 3, 4])` produce?
#List Comprehensions — Elegant One-Liners
A list comprehension lets you create a new list by transforming or filtering an existing one, all in a single line:
# Without comprehension (4 lines)
squares = []
for x in range(10):
squares.append(x ** 2)
# With comprehension (1 line -- same result)
squares = [x ** 2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81][expression for item in iterable]#With a Condition (Filtering)
# Only even squares
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]
# Filter words longer than 3 characters
words = ["I", "am", "learning", "Python", "this", "year"]
long_words = [w for w in words if len(w) > 3]
print(long_words) # ['learning', 'Python', 'this', 'year']
# Convert temperatures
fahrenheit = [32, 68, 77, 95, 212]
celsius = [(f - 32) * 5/9 for f in fahrenheit]
print([f"{c:.1f}" for c in celsius]) # ['0.0', '20.0', '25.0', '35.0', '100.0']#Dictionary and Set Comprehensions
# Dictionary comprehension
word_lengths = {word: len(word) for word in ["Python", "code", "list", "data"]}
print(word_lengths) # {'Python': 6, 'code': 4, 'list': 4, 'data': 4}
# Set comprehension
first_letters = {word[0].lower() for word in ["Python", "programming", "apple", "awesome"]}
print(first_letters) # {'p', 'a'}#Code Playground — Student Grade Tracker
Tests · Run to see averages and grades. Then try each challenge!
#Choosing the Right Data Structure
Here is a quick cheat sheet:
| Structure | Ordered? | Mutable? | Duplicates? | Use When... |
|---|---|---|---|---|
List [] | Yes | Yes | Yes | You need an ordered collection you can modify (most common) |
Tuple () | Yes | No | Yes | Data should not change (coordinates, return values, dict keys) |
Dict {} | Yes* | Yes | Keys: No, Values: Yes | You need to look up values by a meaningful name/key |
Set {} | No | Yes | No | You need unique items, fast membership checks, or set math |
*Dicts preserve insertion order since Python 3.7.
#Putting All Four Together
# Survey answers: a list of dictionaries
reviews = [
{"text": "Great movie!", "rating": 5},
{"text": "Terrible film.", "rating": 1},
{"text": "Loved every minute.", "rating": 5},
]
# The unique words used, as a set (no duplicates)
unique_words = set()
for review in reviews:
for word in review["text"].lower().split():
unique_words.add(word)
print(f"Unique words: {unique_words}")
print(f"How many: {len(unique_words)}")
# Settings are a dictionary
settings = {
"theme": "dark",
"font_size": 14,
"autosave": True,
"language": "en",
}
# Records that should not change: tuples (title, year, rating)
films = [
("Great movie!", 2019, 5),
("Terrible film.", 2021, 1),
]
for title, year, rating in films:
print(f"'{title}' ({year}) -> {rating} stars")#Key Takeaways
- Lists are ordered, mutable collections — access items by index (
list[0]), slice withlist[start:stop], and use methods like.append(),.sort(),.pop(). They are the workhorse of Python - Tuples are immutable lists — use them when data should not change, like coordinates
(x, y)or function return values. Unpack witha, b = my_tuple - Dictionaries store key-value pairs — access by key (
dict["name"]), iterate with.items(). They power JSON and every API you will ever use - Sets contain unique items with fast lookups — use them for deduplication and membership testing. Support union (
|), intersection (&), and difference (-) - List comprehensions create lists in one line —
[expression for item in iterable if condition]is cleaner and faster than a manual loop
What does [10, 20, 30, 40, 50][1:4] return?
This program stores a 2D point as a tuple, then tries to move it by reassigning its x-coordinate. It crashes with a TypeError. Fix it without changing how the point is stored.
Before: (3, 4) After: (5, 4)
Inventory tracker — pick the right structure for each job
Build a tiny inventory system. Use the RIGHT data structure for each piece: 1. `unique_skus` — a SET of all SKUs ever seen (no duplicates). 2. `quantities` — a DICT mapping SKU to current quantity. 3. `log` — a LIST of every transaction as tuples (sku, change). Write a function `record(sku, change)` that: - adds `sku` to `unique_skus` - updates `quantities[sku]` by `change` (positive = stock in, negative = stock out) - appends `(sku, change)` to `log` Then print all three after a few transactions.
unique SKUs: {'A1', 'B2'}
quantities: {'A1': 8, 'B2': 3}
log: [('A1', 10), ('B2', 5), ('A1', -2), ('B2', -2)]unique_skus = set()
quantities = {}
log = []
def record(sku, change):
# TODO: update unique_skus, quantities, and log
pass
record("A1", 10)
record("B2", 5)
record("A1", -2)
record("B2", -2)
print("unique SKUs:", unique_skus)
print("quantities:", quantities)
print("log:", log)