Checkpoint: Data Structures

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.

Learn Checkpoint: Data Structures in our free Python course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

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.

Here's the toolkit from this unit, with the one-line job each tool does best:

Build a tiny text-analysis tool that ties the unit together. Given a paragraph, it should:

Fill in the blanks in the starter below, run it, then compare against the full solution.

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.

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 .

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.

🚀 Up next: Abstract Base Classes (abc) — define interfaces your classes must follow.

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)

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?

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.