Checkpoint: Data Structures
Reviewed & published by Brayan K
A checkpoint is a consolidation lesson that ties an entire unit together, and this one combines everything from the data-structures stretch — dict methods, Counter, defaultdict, deque, heapq, bisect, numbers, strings, bytes, and iterators — into one build challenge and a quiz.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
No new theory here — just proof that the pieces work together. Finish this and you've genuinely mastered Python's core data toolkit.
What This Checkpoint Covers
- Recapping every data-structures tool
- Combining Counter and heapq for top-k
- Choosing the right structure for a job
- A multi-step build challenge
- A full worked solution to compare against
- A 6-question checkpoint quiz
1 Everything You've Learned, At a Glance
Here's the toolkit from this unit, with the one-line job each tool does best:
| Tool | Reach for it when… |
|---|---|
| dict.get / setdefault | Reading or building values without a KeyError |
| Counter | Tallying items and finding the most common |
| defaultdict | Grouping into lists or counting with auto-defaults |
| deque | Fast O(1) queues, stacks, and sliding windows |
| heapq | Priority queues and top-k in O(log n) |
| bisect | Binary search and inserting into sorted lists |
| Decimal / Fraction | Exact money math and exact ratios |
| encode / decode | Converting text to bytes and back with UTF-8 |
| __iter__ / __next__ | Making your own objects loopable |
2 🛠️ Build Challenge: Word-Frequency Top-K Tool
Build a tiny text-analysis tool that ties the unit together. Given a paragraph, it should:
- Normalise the text (lowercase, strip punctuation) and split into words.
- Count word frequencies with Counter.
- Find the top-k most frequent words with heapq.nlargest.
- Group words by length with defaultdict(list).
Fill in the blanks in the starter below, run it, then compare against the full solution.
# 🎯 BUILD CHALLENGE (starter): fill in the ___ blanks
import heapq
from collections import Counter, defaultdict
text = """the quick brown fox the lazy dog the quick fox
ran fast the dog slept the fox watched the quick dog"""
# 1. Normalise: lowercase + split on whitespace
words = text.lower().split()
# 2. Count frequencies
counts = ___(words) # which class tallies an iterable?
# 3. Top-3 most frequent (by count). Use nlargest with a key.
top3 = heapq.___(3, counts.items(), key=lambda kv: kv[1])
# 4. Group unique words by their length
by_len = defaultdict(___) # factory that starts new keys as a list
for w in set(words):
by_len[len(w)].append(w)
print("Top 3:", top3)
print("Lengths present:", sorted(by_len))
# Expected output:
# Top 3: [('the', 6), ('quick', 3), ('fox', 3)]
# Lengths present: [3, 4, 5, 7]# FULL SOLUTION
import heapq
from collections import Counter, defaultdict
text = """the quick brown fox the lazy dog the quick fox
ran fast the dog slept the fox watched the quick dog"""
# 1. Normalise and split
words = text.lower().split()
# 2. Count frequencies with Counter
counts = Counter(words)
# 3. Top-3 by count via heapq.nlargest (ties broken by insertion order)
top3 = heapq.nlargest(3, counts.items(), key=lambda kv: kv[1])
# 4. Group unique words by length with defaultdict(list)
by_len = defaultdict(list)
for w in set(words):
by_len[len(w)].append(w)
print("Top 3:", top3)
print("Lengths present:", sorted(by_len))
# Bonus: Counter already has most_common(), same result:
print("Via most_common:", counts.most_common(3))
# Output:
# Top 3: [('the', 6), ('quick', 3), ('fox', 3)]
# Lengths present: [3, 4, 5, 7]
# Via most_common: [('the', 6), ('quick', 3), ('fox', 3)]Notice Counter.most_common(3) gives the same top-3 directly — but the heapq.nlargest version generalises to any iterable of (item, score) pairs, which is why it's worth knowing both.
3 📝 Checkpoint Quiz
1. Which method reads a dict key with a fallback but does not insert it?
dict.get(key, default). It returns the default without modifying the dictionary. setdefault would insert the key.
2. You need fast removal from the front of a long queue. List or deque, and why?
deque. Its popleft() is O(1), while a list's pop(0) is O(n) because every element shifts down.
3. What does heapq.heappop(heap) always return?
The smallest element — heapq is a min-heap. For the largest, negate values or use heapq.nlargest.
4. Why must a list be sorted before calling bisect.bisect_left?
bisect relies on order to binary-search. On an unsorted list it returns a wrong index silently — no error — which makes the bug hard to spot.
5. Why use Decimal("0.1") instead of Decimal(0.1) for money?
Building from the float 0.1 imports the float's binary rounding error. Building from the string "0.1" stores exactly 0.1.
6. Which two dunder methods make an object usable in a for loop, and what does the second raise when done?
__iter__ (returns the iterator) and __next__ (returns the next value). When exhausted, __next__ raises StopIteration.
📚 Keep Learning
- Python docs: collections — container datatypes
- Python tutorial: Data Structures
- Python docs: Data Types overview
Frequently Asked Questions
Checkpoint cleared — you've mastered Python's data toolkit!
You can recall what every tool does, combine Counter, heapq, and defaultdict into a working tool, pick the right structure for any job, and you've checked your understanding against a full solution and a quiz. That's a serious foundation.
Practice quiz
Which method reads a dict key with a fallback but does NOT insert it?
- dict.setdefault(key, default)
- dict.pop(key, default)
- dict.get(key, default)
- dict[key]
Answer: dict.get(key, default). dict.get returns the default without modifying the dictionary; setdefault would insert the key if it is missing.
You need fast removal from the FRONT of a long queue. List or deque?
- deque — popleft() is O(1)
- list — pop(0) is O(1)
- Either one performs identically
- list — but only if it is sorted
Answer: deque — popleft() is O(1). deque.popleft() is O(1), while a list's pop(0) is O(n) because every remaining element must shift down by one.
What does heapq.heappop(heap) always return?
- The largest element
- The most recently pushed element
- A random element
- The smallest element
Answer: The smallest element. heapq is a min-heap, so heappop returns the smallest element. For the largest, negate values or use heapq.nlargest.
Why must a list be sorted before calling bisect.bisect_left?
- bisect raises an error on unsorted lists
- bisect relies on order to binary-search and returns a wrong index silently otherwise
- It sorts the list for you automatically
- Sorting is only needed for bisect_right
Answer: bisect relies on order to binary-search and returns a wrong index silently otherwise. bisect performs a binary search, which assumes order. On an unsorted list it gives a wrong index with no error, making the bug hard to spot.
Why use Decimal("0.1") instead of Decimal(0.1) for money?
- Decimal(0.1) imports the float's binary rounding error; the string stores exactly 0.1
- The string version is faster
- Decimal(0.1) raises a TypeError
- There is no difference
Answer: Decimal(0.1) imports the float's binary rounding error; the string stores exactly 0.1. Passing the float 0.1 carries over its inexact binary representation; building from the string "0.1" stores exactly one tenth.
Which two dunder methods make an object usable in a for loop?
- __len__ and __getitem__
- __enter__ and __exit__
- __iter__ and __next__
- __call__ and __next__
Answer: __iter__ and __next__. __iter__ returns the iterator and __next__ returns each value; when exhausted, __next__ raises StopIteration.
Which collection is best for tallying items and finding the most common?
- deque
- Counter
- bisect
- Fraction
Answer: Counter. Counter tallies an iterable into counts and offers most_common(n) to return the n highest-frequency items.
What argument do you pass to defaultdict so missing keys start as an empty list?
- defaultdict([])
- defaultdict("list")
- defaultdict(None)
- defaultdict(list)
Answer: defaultdict(list). You pass the factory list (the callable itself, not a call), so each new key is initialized by calling list() to make a fresh empty list.
What does "café".encode("utf-8") return?
- A str
- A bytes object
- A list of ints
- A Counter
Answer: A bytes object. encode turns a str into a bytes object using the given codec; decode reverses it back into a str.
What does heapq.nlargest(3, items, key=...) give you?
- The 3 smallest items
- All items sorted ascending
- The 3 largest items by the key, as a list
- A single largest item
Answer: The 3 largest items by the key, as a list. nlargest returns the n highest-scoring items (by the key function) as a list, generalizing top-k to any iterable of values.
Continue this course
- Previous: Iterators: __iter__ & __next__
- Next: Abstract Base Classes (abc) — Define and enforce interfaces with abc.ABC and @abstractmethod
- Quick reference: Python cheat sheet
Frequently asked questions
What is this checkpoint testing?
This checkpoint pulls together the whole data-structures stretch: dictionary methods, Counter and defaultdict, deque, heapq, bisect, the numeric types, strings versus bytes, and the iterator protocol. The build challenge combines several of them at once, the way real programs do, so you can confirm the pieces fit together in your head — not just individually.
How should I use the build challenge?
Try the starter version yourself first, filling in the blanks before you reveal anything. Only open the full solution once you've made a genuine attempt or you're truly stuck. The struggle is where the learning happens; reading a finished answer cold teaches far less than wrestling with it for a few minutes first.
What if I get a quiz question wrong?
That's exactly what a checkpoint is for — to surface gaps while they're cheap to fix. Note which topic the question came from, reopen that lesson, redo its Try It Yourself snippets, then come back. A wrong answer here is worth more than a right one, because it points you to precisely what to review.
Am I ready for the next lesson after this?
If you can complete the build challenge with the solution mostly hidden and answer most of the quiz, yes — you're ready for Abstract Base Classes. If several pieces feel shaky, spend a little time revisiting those specific lessons first. Solid foundations make the more advanced object-oriented material that follows much easier.