Lesson 20 of 34 · 0 of 2 exercises solved

collections & itertools

Two standard- that turn ten-line into one-liners: collections for smarter containers, itertools for smarter iteration.

Counter counts things. It is a dict subclass, so counts["x"] works, missing keys read as 0, and most_common(n) hands you the leaderboard.

from collections import Counter

words = "red blue red green red blue yellow".split()
counts = Counter(words)
print(counts)
print(counts["red"], counts["nope"])
print(counts.most_common(2))

letters = Counter("mississippi")
print(letters.most_common(3))
print(Counter(a=3, b=1) + Counter(a=1, c=2))
Python (loads on first run)Ctrl+Enter runs

defaultdict creates a missing value on first access, which removes the "if key not in d" dance when grouping. deque is a double-ended with O(1) appends and pops at both ends — the right structure for sliding windows and breadth-first search.

from collections import defaultdict, deque

by_first_letter = defaultdict(list)
for name in ["Ada", "Alan", "Maya", "Mark", "Sam"]:
    by_first_letter[name[0]].append(name)
print(dict(by_first_letter))

window = deque(maxlen=3)
for reading in [12, 15, 11, 18, 20]:
    window.append(reading)
    print(list(window), round(sum(window) / len(window), 1))

queue = deque(["email", "lunch", "review"])
queue.appendleft("standup")
print(queue.popleft(), queue.pop(), list(queue))
Python (loads on first run)Ctrl+Enter runs

namedtuple gives you lightweight records with named fields (use a when you need or mutability). ChainMap layers dicts so lookups fall through, ideal for defaults + overrides.

from collections import namedtuple, ChainMap

Point = namedtuple("Point", "x y")
p = Point(3, 4)
print(p, p.x, p[1], p._asdict())

defaults = {"theme": "light", "sounds": True}
user = {"theme": "dark"}
settings = ChainMap(user, defaults)
print(settings["theme"], settings["sounds"])
Python (loads on first run)Ctrl+Enter runs

itertools composes lazily. chain ; islice any iterator; product, permutations and combinations arrangements; groupby clusters consecutive equal keys; accumulate produces running totals.

from itertools import chain, islice, product, combinations, groupby, accumulate, count

print(list(chain([1, 2], (3, 4), "ab")))
print(list(islice(count(10, 5), 4)))            # 10 15 20 25
print(list(product("ab", [1, 2])))
print(list(combinations(["bread", "cheese", "olives"], 2)))
print(list(accumulate([100, 20, -30, 50])))

team = [("design", "Maya"), ("design", "Ada"), ("support", "Sam"), ("design", "Leo")]
for dept, group in groupby(sorted(team), key=lambda m: m[0]):
    print(dept, [name for _, name in group])
Python (loads on first run)Ctrl+Enter runs

Common question

Do I need the special containers when dict and list already exist?

The collections module gives you containers shaped for common jobs: Counter for tallies, defaultdict for grouping, deque for queues and sliding windows, namedtuple for light records. itertools gives you lazy building blocks for looping: chaining, slicing, grouping, combining. Together they replace many small hand-written loops with one named, tested operation.

Learn the handful you will use every week (Counter, defaultdict, deque, chain, islice, groupby) and reach for them whenever a loop starts with "make an empty thing, then for each item...". The result is shorter, faster and, because the name says what it does, easier to read.

Source: The Zen of Python: "There should be one — and preferably only one — obvious way to do it." For counting, the obvious way is Counter.

Common mix-ups

Each of these sounds reasonable. Think about why it is wrong, then open the explanation.

  • Anything Counter does can be done with a dict and a loop, so Counter is redundant.

  • A is as good as a deque for a queue.

  • itertools build lists, so they use as much as a .

Practice

Write group_by_length(words) that returns a dict mapping each length to the list of words of that length, in original order. Then write top_letters(text, n) that returns the n most common letters (ignoring case and non-letters) as (letter, count) .

Exercise

Group and count

4 checks
from collections import defaultdict, Counter


def group_by_length(words):
    ...


def top_letters(text, n):
    ...


print(group_by_length(["red", "to", "yellow", "blue", "sky"]))
print(top_letters("Hello, balloon!", 2))
 
Python (loads on first run)Ctrl+Enter runs
Output
Press Run to try your code, then Check answer to grade it.

Write moving_average(values, size) that returns a list of the average of each window of size consecutive values. Use a deque with maxlen. moving_average([1, 2, 3, 4, 5], 3) gives [2.0, 3.0, 4.0].

Exercise

Sliding window

4 checks
from collections import deque


def moving_average(values, size):
    ...


print(moving_average([1, 2, 3, 4, 5], 3))
 
Python (loads on first run)Ctrl+Enter runs
Output
Press Run to try your code, then Check answer to grade it.

Check yourself

Quick check

What does Counter("banana")["z"] return?

Finished reading?2 exercises still open. You can come back to them later.

Tip: use the ← and → keys to move between lessons.