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
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))
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:
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))
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:
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))
{'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:
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)
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:
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)
Point(x=3, y=4) 7
{'x': 3, 'y': 4}
3 4ChainMap: layered lookups
A ChainMap searches several dictionaries in order — ideal for settings with layers of overrides:
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"])
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?
| Task | Use |
|---|---|
| Count how often things occur | Counter |
| Group items into lists by key | defaultdict(list) |
| Queue, or keep the last N items | deque (with maxlen) |
| Lightweight immutable record | namedtuple / typing.NamedTuple |
| Defaults with overrides | ChainMap |