What are `defaultdict`, `Counter`, `deque`, and `namedtuple` used for?

6 minintermediatecollectionsdefaultdictcounterdequenamedtuple

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.