I ignored collections for years because the name sounds like the module equivalent of a junk drawer. It's the opposite: a shelf of containers that each replace ten lines of hand-rolled bookkeeping with three lines that say what they mean. These are the four I actually use.
from collections import Counter
words = "spam eggs spam bacon spam eggs".split()
counts = Counter(words)
counts.most_common(1) # [('spam', 3)]
Reach for it when you're about to write the manual count loop — the one with if word in counts: in it. Counter just counts, and most_common(n) gives you the leaderboard for free. It's a dict underneath, so counts["spam"] works, and asking for a key that never appeared returns 0 instead of raising.
from collections import defaultdict
by_ext = defaultdict(list)
for name in ["a.py", "b.txt", "c.py"]:
by_ext[name.split(".")[-1]].append(name)
# {'py': ['a.py', 'c.py'], 'txt': ['b.txt']}
Reach for it when you're grouping things: a missing key creates an empty list instead of raising KeyError. My hand-written version of this loop had an if ext not in by_ext: branch and still crashed on the one filename without a dot in it.
from collections import deque
window = deque(maxlen=3)
for n in [4, 8, 15, 16, 23]:
window.append(n)
# deque([15, 16, 23])
With maxlen set, appending to a full deque silently drops the oldest item — a rolling window in two lines, which is how I keep "the last N lines of the log" and how I'd tail a growing file without it growing in memory. It's also the queue container: popleft() is O(1), while list.pop(0) shifts every remaining element. Reach for it whenever the front of the line matters as much as the back.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
x, y = p # unpacks like a tuple
print(p) # Point(x=3, y=4)
Reach for it when a dict always has the same three keys and you're tired of row["lat"] and the one typo that comes with it. You get attribute access, unpacking, and a readable repr, and the objects are immutable — nobody downstream can rename a field by accident.
Real scripts rarely use just one. Tallying what's in a folder, for instance:
from pathlib import Path
from collections import Counter
exts = Counter(f.suffix.lower() for f in Path(".").iterdir() if f.is_file())
print(exts.most_common(3))
A Counter fed by a Path generator answers in two lines what I used to answer by opening the folder and squinting. If you're unsure which to reach for: Counter for counting, defaultdict for grouping, deque when order matters, namedtuple for rows.
The test I use for whether something in the standard library earns its keep: did it delete code, or just relabel it? Every one of these four deleted code I had genuinely written by hand, badly. That's the whole recommendation.