Lesson 31 · Intermediate · 19 min

Python collections

Reach for the right container instead of rebuilding it by hand.

collectionsCounterdefaultdictdeque

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.
Your turn

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.

In-browser PythonRuns locally · no account
First run downloads Pyodide from a CDN. Use Run code after editing.

Practice this lesson

12 questions with immediate explanations.

See all 12 answersStart practice
← itertoolsfunctools →