The collections Module

The standard library's collections module adds specialised containers for jobs that come up again and again: counting things, grouping values, fast queues, and records with named fields. Reaching for them makes common code shorter and faster.

Counter: counting things

PY counter.py
from collections import Counter

words = "the cat and the hat and the bat".split()
counts = Counter(words)

print(counts)
print(counts["the"], counts["dog"])
print(counts.most_common(2))
Output
Counter({'the': 3, 'and': 2, 'cat': 1, 'hat': 1, 'bat': 1})
3 0
[('the', 3), ('and', 2)]

A Counter is a dictionary of counts. Missing keys return 0 instead of raising an error, and most_common(n) gives the top items. Counters can be combined with arithmetic:

PY counter_math.py
from collections import Counter

monday = Counter(apples=3, pears=2)
tuesday = Counter(apples=1, plums=4)

print(monday + tuesday)
print(monday - tuesday)
print(sum((monday + tuesday).values()))
print(Counter("mississippi").most_common(1))
Output
Counter({'apples': 4, 'plums': 4, 'pears': 2})
Counter({'apples': 2, 'pears': 2})
10
[('i', 4)]

defaultdict: automatic defaults

A defaultdict creates a value for a missing key automatically, using the function you give it:

PY defaultdict.py
from collections import defaultdict

orders = [("Ana", "tea"), ("Ben", "cake"), ("Ana", "scone"), ("Cleo", "tea")]

by_customer = defaultdict(list)
for customer, item in orders:
    by_customer[customer].append(item)
print(dict(by_customer))

totals = defaultdict(int)
for customer, _ in orders:
    totals[customer] += 1
print(dict(totals))
Output
{'Ana': ['tea', 'scone'], 'Ben': ['cake'], 'Cleo': ['tea']}
{'Ana': 2, 'Ben': 1, 'Cleo': 1}

defaultdict(list) starts each new key with an empty list; defaultdict(int) starts with 0. No more if key not in d checks.

deque: fast queues

A deque ("deck", double-ended queue) adds and removes items quickly at both ends. Lists are slow at removing from the front, because every other item has to shift:

PY deque.py
from collections import deque

queue = deque(["ana", "ben"])
queue.append("cleo")
queue.appendleft("vip")
print(queue)

print(queue.popleft(), queue.pop())
print(queue)

recent = deque(maxlen=3)
for page in ["home", "about", "blog", "contact", "faq"]:
    recent.append(page)
print(list(recent))

d = deque([1, 2, 3, 4, 5])
d.rotate(2)
print(d)
Output
deque(['vip', 'ana', 'ben', 'cleo'])
vip cleo
deque(['ana', 'ben'])
['blog', 'contact', 'faq']
deque([4, 5, 1, 2, 3])

With maxlen, a deque keeps only the most recent items — perfect for "last N" histories. Use a deque whenever you need a first-in-first-out queue.

namedtuple

namedtuple creates a tuple subclass with named fields — the older, function-style version of typing.NamedTuple from lesson 22:

PY namedtuple.py
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)

print(p, p.x + p.y)
print(p._asdict())
x, y = p
print(x, y)
Output
Point(x=3, y=4) 7
{'x': 3, 'y': 4}
3 4

ChainMap: layered lookups

A ChainMap searches several dictionaries in order — ideal for settings with layers of overrides:

PY chainmap.py
from collections import ChainMap

defaults = {"theme": "light", "lang": "en", "font": 14}
user = {"theme": "dark"}
session = {"font": 18}

settings = ChainMap(session, user, defaults)
print(settings["theme"], settings["lang"], settings["font"])
Output
dark en 18

OrderedDict

Regular dictionaries have kept insertion order since Python 3.7, so OrderedDict is rarely needed now. It still offers move_to_end() and order-sensitive equality, which can be handy for things like simple caches.

Which one do I need?

TaskUse
Count how often things occurCounter
Group items into lists by keydefaultdict(list)
Queue, or keep the last N itemsdeque (with maxlen)
Lightweight immutable recordnamedtuple / typing.NamedTuple
Defaults with overridesChainMap

Try it yourself

Try it yourself
Console output