Chapter 3: Lists, Tuples & Collections - Python's Data Containers Unleashed¶
Imagine you're organizing a party. Lists are your flexible guest list - people can arrive, leave, swap positions. Tuples? That's your venue address - it doesn't change once set. Dictionaries are your seating chart - each guest has their assigned spot. Sets? The actual people who showed up - no duplicates, no order, just unique individuals.
Let's throw this party.
Lists: Your Shape-Shifting Array¶
Lists in Python are what arrays wish they could be in other languages. Dynamic sizing? Check. Mixed types? Sure, why not. Negative indexing? You bet.
Indexing: Forward, Backward, and Everywhere¶
You can access from the front or the back:
fruits = ["apple", "banana", "cherry", "date"]
print(fruits[0]) # "apple" - the beginning
print(fruits[-1]) # "date" - the end
print(fruits[-2]) # "cherry" - second from end
Negative indexing is brilliant. No more fruits[len(fruits)-1] nonsense. Just grab from the back.
Slicing: The Swiss Army Knife¶
Here's where Python shows off:
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers[2:5] # [2, 3, 4] - from index 2 to 5 (exclusive)
numbers[:3] # [0, 1, 2] - first three
numbers[7:] # [7, 8, 9] - from 7 to end
numbers[-3:] # [7, 8, 9] - last three
numbers[::2] # [0, 2, 4, 6, 8] - every second element
numbers[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - REVERSED!
That last one? [::-1] to reverse? That's Python flexing. The syntax is [start:stop:step]. Negative step means go backwards.
Want to copy a list? new_list = old_list[:]. Want every third element starting from index 1? list[1::3]. Slicing is poetry.
List Methods: Add, Remove, Rearrange¶
items = [1, 2, 3]
items.append(4) # [1, 2, 3, 4] - add to end
items.insert(0, 0) # [0, 1, 2, 3, 4] - insert at index
items.extend([5, 6]) # [0, 1, 2, 3, 4, 5, 6] - add multiple
items.remove(3) # removes first occurrence of 3
items.pop() # removes and returns last item
items.pop(0) # removes and returns item at index 0
Important distinction: append() adds one item. extend() adds multiple items from an iterable. Don't do items.append([5, 6]) unless you want a nested list.
items.sort() # sorts in place
items.reverse() # reverses in place
len(items) # how many items?
items.count(2) # how many times does 2 appear?
items.index(4) # what's the index of 4?
sort() and reverse() modify the list directly. They return None, not a new list. If you want a new sorted list, use sorted(items).
List Comprehensions: The Game Changer¶
This is where Python goes from "nice language" to "I'm never going back."
Instead of:
Write this:
One line. Clear. Readable. Fast.
You can add conditions:
Transform and filter:
words = ["hello", "world", "python", "is", "amazing"]
long_upper = [w.upper() for w in words if len(w) > 5]
# ["PYTHON", "AMAZING"]
Nested loops? Yes:
pairs = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]
List comprehensions are faster than loops and more Pythonic. Use them. Master them. Love them.
Tuples: The Immutable Twin¶
Tuples look like lists but use parentheses. The real difference? You can't change them.
Why would you want immutability?
Reason 1: It's safer. You can't accidentally modify data that shouldn't change.
Reason 2: Tuples can be dictionary keys (lists can't).
Reason 3: They're slightly faster and use less memory.
Tuple Packing and Unpacking¶
This is where tuples shine:
# Packing
point = 10, 20, 30 # parentheses optional
# Unpacking
x, y, z = point
print(x) # 10
# Swap variables (no temp variable needed!)
a, b = 5, 10
a, b = b, a # a is now 10, b is now 5
Functions returning multiple values? They're returning tuples:
This unpacking works with lists too, but tuples are the idiomatic choice for fixed-size collections.
Single Element Tuples: The Comma Matters¶
not_a_tuple = (5) # this is just 5 with parentheses
actual_tuple = (5,) # this is a tuple with one element
That trailing comma? It's what makes it a tuple. Weird, but that's the syntax.
Sets: The Unique-Only Club¶
Sets store unique values. No duplicates. No guaranteed order.
Why Use Sets?¶
Removing duplicates:
Fast membership testing:
allowed_users = {"alice", "bob", "charlie"}
if "alice" in allowed_users: # O(1) - instant lookup
grant_access()
Checking if item in list is slow (checks every element). Checking if item in set is instant.
Set Operations: Math Made Practical¶
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # union: {1, 2, 3, 4, 5, 6}
a & b # intersection: {3, 4}
a - b # difference: {1, 2}
a ^ b # symmetric difference: {1, 2, 5, 6}
These operations are incredibly useful for finding common elements, differences, or combinations.
Dictionaries: The Real MVP¶
We'll give dictionaries their own chapter because they deserve it. But here's the teaser: if lists are Python's bread and butter, dictionaries are the entire meal.
What You Just Learned:
- Lists are flexible, mutable, and slice-able masterpieces
- [::-1] reverses anything
- List comprehensions replace loops with elegance
- Tuples are immutable and perfect for unpacking
- Sets eliminate duplicates and enable lightning-fast lookups
- Slicing syntax: [start:stop:step]
Coming up: Dictionaries - where Python becomes a superpower. You thought lists were cool? Wait until you see what key-value pairs can do.
Your Challenge: Before moving on, write a list comprehension that takes a string and returns a list of vowels found in it. One line. Go.
Did you get it? If yes, you're ready for dictionaries. If not, read this chapter again. List comprehensions are that important.