Maya wants the best-selling drink, the waiting queue, and a count by size. Three loops would do it; three containers do it more clearly. Which one counts without a dict.get?
The collections module provides containers for jobs that appear constantly: counting, grouping by a key, and adding or removing at both ends. Each one is a small, focused tool that makes the intent of the code easier to see. You will also keep a bounded window, layer settings, and compare inventories.
Count values with Counter
Counter is a dictionary subclass that counts occurrences. Missing keys return zero instead of raising KeyError. most_common returns the highest counts first, which is useful for summaries and rankings.
from collections import Counter
votes = ["tea", "coffee", "tea"]
counts = Counter(votes)
print(counts["tea"])
print(counts.most_common(1))Count first, rank second
Group with defaultdict
defaultdict calls a factory when a missing key is accessed. With list as the factory, every new key starts with an empty list. This removes the repeated check for an existing key before appending.
from collections import defaultdict
groups = defaultdict(list)
for name, team in [("Ada", "A"), ("Grace", "B"), ("Lin", "A")]:
groups[team].append(name)
print(dict(groups))A list for every new key
Use deque for both ends
A deque supports fast appends and removals at either end. popleft removes the oldest item from the front, while append adds to the back. This is a natural fit for queues and sliding windows.
from collections import deque
queue = deque(["a", "b"])
queue.append("c")
queue.popleft()
print(list(queue))A queue with two ends
Keep a bounded rolling window
A deque with maxlen drops the oldest item automatically. That makes it a good fit for a moving average or a short activity feed: no slice copy and no index bookkeeping. The window size is fixed when the deque is created.
from collections import deque
window = deque(maxlen=3)
for reading in [10, 12, 11, 15]:
window.append(reading)
print(list(window), round(sum(window) / len(window), 1))Old values leave as new values arrive
Layer configuration with ChainMap
ChainMap checks its maps from left to right. Put user settings first and defaults last, and a missing user key falls through without copying the dictionaries. new_child() creates a temporary override layer for one request or one screen.
from collections import ChainMap
defaults = {"theme": "light", "page_size": 20}
user = {"theme": "dark"}
settings = ChainMap(user, defaults)
print(settings["theme"], settings["page_size"])
print("page_size" in user)
scoped = settings.new_child({"page_size": 50})
print(scoped["page_size"], settings["page_size"])Overrides without merging copies
Counter arithmetic keeps the positive overlap
Subtracting one Counter from another drops zero and negative counts. That is useful for remaining stock, but it means the result is not a complete ledger. Intersection keeps the smaller count for each key, which answers a different question: how much do two inventories have in common?
from collections import Counter
stock = Counter({"tea": 3, "latte": 2})
sold = Counter({"tea": 2, "latte": 2, "mocha": 1})
left = stock - sold
print(left)
print(list(left.elements()))
print(stock & sold)Remaining stock versus shared stock
Key points
- Counter counts hashable values and returns zero for missing keys.
- most_common orders entries by descending count.
- defaultdict creates a default value for a missing key.
- deque supports efficient operations at both ends.
- Convert specialized containers to plain lists or dictionaries when leaving the local code.
- Use maxlen for rolling windows; ChainMap for precedence; Counter arithmetic for inventory views.
Common mistakes
- Using counts[key] on a plain dictionary when the key may be missing.
- Forgetting that defaultdict also inserts the key when it is read.
- Using list.pop(0) repeatedly when a deque is the better queue.
- Treating Counter subtraction as a full audit trail: zero and negative results disappear.
Count the words in ['a', 'b', 'a'] with Counter, then add a new team to a defaultdict(list).
Try it yourself
Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.
Practice this lesson
12 questions with immediate explanations.