What are `defaultdict`, `Counter`, `deque`, and `namedtuple` used for?
Quick Answer
`collections.defaultdict(factory)` auto-creates a default value for missing keys, removing manual `if key not in d` checks. `Counter` is a dict subclass specialized for counting hashable items, with `.most_common()`. `deque` is a double-ended queue with O(1) append/pop from **both ends** (unlike `list`, which is O(n) at the front). `namedtuple` creates lightweight, immutable, attribute-accessible tuple subclasses for simple records.
Detailed Answer
defaultdict: no more if key not in d
from collections import defaultdict
groups = defaultdict(list)
for name in ["ada", "amy", "bob", "ben"]:
groups[name[0]].append(name)
# groups = {'a': ['ada', 'amy'], 'b': ['bob', 'ben']}
Without defaultdict, every append needs a manual check:
if name[0] not in groups: groups[name[0]] = []. defaultdict(list)
calls list() automatically the first time a missing key is accessed,
eliminating that boilerplate.
Counter: counting made trivial
from collections import Counter
words = "the quick brown fox the lazy dog the".split()
counts = Counter(words)
counts["the"] # 3
counts.most_common(2) # [('the', 3), ('quick', 1)] -- ties broken by insertion order
counts + Counter(["fox"]) # supports arithmetic between Counters
Counter is a dict subclass where missing keys default to 0 (instead
of raising KeyError), plus convenience methods like .most_common() and
multiset-style arithmetic (+, -, &, |).
deque: O(1) at both ends
from collections import deque
dq = deque([1, 2, 3])
dq.appendleft(0) # O(1) -- list.insert(0, x) would be O(n)
dq.append(4) # O(1)
dq.popleft() # O(1) -- list.pop(0) would be O(n)
dq = deque(maxlen=3) # bounded deque -- great for "last N items" buffers
deque is implemented as a doubly-linked list of fixed-size blocks, so
both ends support O(1) operations — the natural choice for queues, sliding
windows, and BFS traversal, where list.pop(0)/insert(0, x) would be a
performance trap (O(n) each).
namedtuple: lightweight, immutable records
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
p.x, p.y # 1, 2 -- attribute access
p[0], p[1] # 1, 2 -- still a tuple, so positional access works too
p == Point(1, 2) # True -- structural equality, generated automatically
namedtuple generates a tuple subclass with named fields — you get
attribute access and tuple behavior (unpacking, indexing, hashability),
at essentially zero extra memory over a plain tuple. It's the natural
choice before reaching for a full @dataclass when the type is small,
immutable, and truly tuple-like.
Interview-ready summary: defaultdict removes manual missing-key
checks, Counter is a purpose-built counting dict, deque gives O(1)
operations at both ends (unlike list's O(n) front operations), and
namedtuple gives cheap, immutable, attribute-accessible records — each
solves a specific, common gap left by the plain list/dict/tuple
built-ins.