So far your programs have juggled values one variable at a time. Real data comes in crowds: a class of students, a month of temperatures, a cart of products. This lesson introduces Python's four built-in collections, lists, tuples, sets, and dictionaries, and then the feature that makes Python code unmistakably Python: comprehensions. Many learners describe this as the lesson where the language clicks, because suddenly the programs you can write stop being exercises and start being useful.
A quiet promise before we start: you do not need to memorize every method on every collection. You need to know what each structure is for, the handful of operations you will use daily, and how to look up the rest. The choosing table in section 5 is the actual takeaway of this lesson; syntax fades in and out of memory, but knowing which container fits a problem is permanent skill.
What you will learn in Part 5
- Lists: indexing, slicing, and the methods you actually use
- Tuples: immutable sequences and the unpacking idiom
- Dictionaries: key-value lookup, the workhorse of real programs
- Sets: instant membership tests and de-duplication
- How to choose the right structure for a problem
- List, dict, and set comprehensions, plus enumerate and zip
Note
Before you start
You need loops from Part 3 and functions from Part 4. Every example here runs in the playground below, and the Data Structures and Comprehensions lessons in the Learn Python app mirror this material topic for topic.
1. Lists: the everything container
A list is an ordered, changeable sequence of values, written in square brackets. Items keep the order you gave them, can be read by position starting from zero, and can be added, removed, and replaced freely. Lists hold anything, numbers, strings, other lists, a mix, though in practice good code keeps one type per list. Negative indexes count from the end, with -1 meaning the last item, an idiom you will use constantly.
marks = [78, 91, 65, 84, 70]
print(marks[0]) # 78 first
print(marks[-1]) # 70 last
print(len(marks)) # 5
marks.append(88) # add at the end
marks.insert(0, 50) # add at a position
marks.remove(65) # remove first matching value
top = max(marks)
marks.sort() # in place, ascending
print(marks) # [50, 70, 78, 84, 88, 91]
print(f"top mark: {top}")
Slicing reads a sub-list with the same start-included, stop-excluded convention as range: marks[1:4] gives items at positions 1, 2, and 3, marks[:3] gives the first three, marks[-2:] the last two, and marks[::-1] the whole list reversed. Slices always produce a new list, leaving the original untouched, which is also the standard way to copy a list: copy = original[:]. That distinction between sharing and copying matters because lists are mutable, and two names can point at one list; if you append through one name, the other sees it too, the same aliasing that made mutable defaults dangerous in Part 4.
2. Tuples: sequences that promise not to change
A tuple is an ordered sequence like a list, but immutable: once created, it cannot grow, shrink, or be modified. Tuples are written with parentheses, or with nothing at all, because it is actually the commas that make a tuple, which is exactly what you used when a Part 4 function returned two values. Use tuples for fixed-shape data, a coordinate pair, a date triple, a record of name and score, and lists for open-ended collections of similar things.
point = (12.5, 7.8)
student = ("Amina", 87) # the record shape from Part 4
name, score = student # unpacking: one name per slot
print(f"{name} scored {score}")
x, y = point
y, x = x, y # the classic Python swap, no temp needed
print(x, y) # 7.8 12.5
Unpacking is the idiom to internalize here. Any sequence can be unpacked into exactly matching names, loops can unpack as they go, and a starred name can soak up the middle: first, *rest = scores. Immutability is not a limitation; it is information. When you see a tuple, you know nothing will mutate it behind your back, which is also why tuples can serve as dictionary keys while lists cannot.
3. Dictionaries: looking things up by name
A dictionary maps keys to values, like a phone book maps names to numbers. It is the single most used data structure in real Python, because most real data is naturally labeled: a user has a name, an email, an age; a product has a price and a stock count. Curly braces with key colon value pairs create one, square brackets read and write by key, and the in operator checks whether a key exists. Reading a missing key with brackets raises KeyError; the get method returns None, or a default you choose, instead, and choosing between those two behaviors deliberately is a mark of code that has been thought through.
student = {"name": "Amina", "score": 87, "city": "Colombo"}
print(student["name"]) # Amina
student["score"] = 91 # update
student["grade"] = "A" # insert new key
print(student.get("email")) # None, no crash
print(student.get("email", "n/a")) # n/a, your default
for key, value in student.items(): # unpacking in the loop header
print(f"{key}: {value}")
Counting things is the dictionary pattern you will use most, an accumulator from Part 3 where the accumulator is a dict. Walk the data; for each item, add one to its entry, creating the entry on first sight with get. This five-line shape powers word frequencies, vote tallies, inventory counts, and the data work coming in Part 11; the playground at the end of this lesson has you build it yourself.
Checkpoint
d = {"a": 1}. What does d["b"] do, and what about d.get("b")?
4. Sets: membership and uniqueness
A set is an unordered collection of unique values. Add the same item twice and it is stored once; ask whether an item is present and the answer comes back essentially instantly, no matter how large the set, which is dramatically faster than searching a long list. The two everyday uses are de-duplication, set(items) collapses repeats in one call, and fast membership testing for lookups you perform in a loop. Sets also support algebra straight out of math class: union, intersection, and difference, which turn questions like "who attended both sessions" into one operator.
monday = {"amina", "zane", "ruwan", "kai"}
tuesday = {"zane", "kai", "neha"}
print(monday & tuesday) # {'zane', 'kai'} both days
print(monday | tuesday) # everyone at least once
print(monday - tuesday) # {'amina', 'ruwan'} Monday only
emails = ["a@x.com", "b@x.com", "a@x.com", "c@x.com", "b@x.com"]
unique = set(emails)
print(f"{len(emails)} signups, {len(unique)} unique") # 5 signups, 3 unique
5. Choosing the right structure
| You need... | Use | Why |
|---|---|---|
| An ordered collection you will modify | list | Keeps order, grows and shrinks, index and slice access |
| A fixed-shape record that must not change | tuple | Immutable, unpackable, usable as a dict key |
| To look values up by a label | dict | Near-instant key access, the natural fit for labeled data |
| Uniqueness or fast "is it there?" checks | set | De-duplicates automatically, membership tests stay fast at any size |
| To count occurrences of things | dict | The counting pattern; Part 8 upgrades it with collections.Counter |
When two structures both seem plausible, ask what the code around them will do. If you will loop in order, list. If you will ask "does X exist" repeatedly, set or dict. If the shape is fixed and meaning comes from position, tuple. Choosing well is not pedantry; the structure you pick decides how every later line reads, and frequently how fast it runs.
6. Comprehensions: building collections in one line
Here is the signature move of Python. The pattern of creating an empty list, looping, and appending transformed items is so common that the language gives it a dedicated syntax. A list comprehension puts the expression first, then the loop, then an optional filter, inside square brackets, and it reads almost like set notation from school: the squares of n for every n in the range, keeping only the even ones. Swap the brackets for braces and you get a set comprehension, add a key colon value and you get a dict comprehension.
numbers = [3, 8, 12, 5, 20, 7]
# The loop way
doubled = []
for n in numbers:
doubled.append(n * 2)
# The comprehension way: same result, one honest line
doubled = [n * 2 for n in numbers]
evens = [n for n in numbers if n % 2 == 0]
squares = {n: n ** 2 for n in numbers} # dict comprehension
lengths = {len(w) for w in ["hi", "hey", "yo"]} # set: {2, 3}
print(doubled) # [6, 16, 24, 10, 40, 14]
print(evens) # [8, 12, 20]
print(squares[5]) # 25
Comprehensions are not about saving keystrokes; they are about saying what instead of how. The reader sees the output expression first and knows the destination before the journey. The discipline that keeps them readable: one loop, at most one if, and no side effects. The moment a comprehension needs two conditions and a nested loop, write the plain for loop instead; seniors do, without embarrassment.
7. enumerate and zip: looping like a local
Two built-ins finish the toolkit. enumerate pairs each item with its index, killing the clumsy range(len(items)) pattern, and zip walks two or more sequences in lockstep, pairing first with first, second with second, stopping at the shortest. Both produce pairs that unpack right in the loop header, the tuple skill from section 2 paying rent already.
names = ["Amina", "Zane", "Ruwan"]
scores = [87, 91, 78]
for i, name in enumerate(names, start=1):
print(f"{i}. {name}")
for name, score in zip(names, scores):
print(f"{name}: {score}")
ranking = dict(zip(names, scores)) # {'Amina': 87, 'Zane': 91, ...}
Checkpoint
What does [w.upper() for w in words if len(w) > 3] produce for words = ["sun", "moon", "sky", "star"]?
8. Practice: a word frequency counter
The playground below builds the classic word frequency counter: split a text into words, count each with a dictionary, then report the top results sorted with a lambda key from Part 4. Every technique from the last three lessons appears in fifteen lines, which is the point: lessons stop being separate the moment you build something. The exercises extend it with a set, a comprehension, and a tuple unpack.
If your counting loop feels verbose, good instinct: Part 8 introduces collections.Counter, which does it in one line, and you will appreciate it precisely because you have built the manual version. That is a pattern this course repeats deliberately: first the principle by hand, then the standard library shortcut, so the shortcut is never magic.
! Common mistakes to avoid
-
✕Copying a list with b = a and being surprised that changing b changes a.
✓Assignment shares the one list between two names. Copy explicitly with b = a[:] or b = list(a) when you need independence.
-
✕Writing result = my_list.sort() and getting None.
✓sort() reorders in place and returns None. Use sorted(my_list) when you want a new sorted list back.
-
✕Removing items from a list while looping over it.
✓You skip neighbors as positions shift. Build the kept items with a comprehension instead: items = [x for x in items if keep(x)].
-
✕Using a list where a set was needed, then wondering why lookups are slow.
✓Checking "x in big_list" scans every element; "x in big_set" is effectively instant. If a loop does membership tests, convert to a set first.
? Frequently asked questions
Do dictionaries keep their order? +
Since Python 3.7, dicts preserve insertion order as a language guarantee, so items come back in the order you added them. Sets make no such promise; never rely on set order.
Can a list hold mixed types? +
Yes, [1, "two", 3.0] is legal. It is still usually a design smell: collections of same-typed items stay easy to process. For fixed mixed-shape records, prefer a tuple, a dict, or, from Part 6, a dataclass.
Are comprehensions faster than loops? +
Usually somewhat, because the loop machinery runs in C. But choose them for readability, not speed; a comprehension that needs a comment to explain has already failed at its one job.
What about nested data, like a list of dicts? +
Completely normal; that is exactly the shape of JSON data, and Part 11 is dedicated to working with it. Everything composes: dicts in lists, lists in dicts, tuples as dict keys.
9. Recap and what comes next
You now command the four containers every Python program is built from, lists for ordered changeable data, tuples for fixed records, dicts for labeled lookup, sets for uniqueness and membership, plus comprehensions, enumerate, zip, slicing, and unpacking. With functions from Part 4 wrapped around them, you can already write genuinely useful programs, and the rest of the course assumes these tools the way it assumes arithmetic.
Next, the course shifts from organizing data to organizing programs: Part 6, object-oriented Python introduces classes, the pattern behind nearly every library you will ever import. Run the playground exercises above first, then get extra reps in the Data Structures and Comprehensions lessons of the Learn Python app below; the full track is always at the series hub.
Pro tip
When you meet an unfamiliar object anywhere in Python, two functions tell you everything: type(thing) says what it is, and dir(thing) lists what it can do. Five seconds of inspection beats five minutes of searching, and it works on every value in the language.
Practice on the go
Learn Python, the free Android app
Every topic in this series lives in the app too: bite-size lessons, runnable examples, quizzes, mini projects, and an offline Python playground that runs on your phone.
Comments
0No comments yet. Be the first to share your thoughts.