The collections Module Interview Questions & Answers
6 questions Updated 2026-06-18
Python interview questions on the collections module — Counter, defaultdict, deque, OrderedDict, ChainMap, and how they relate to namedtuple.
Counter is a dict subclass for counting hashable items. You pass it
any iterable and it tallies occurrences into a {element: count} mapping, with
handy extras like most_common(). Missing keys return 0 instead of raising.
from collections import Counter
c = Counter("mississippi")
# Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
c.most_common(2) # [('i', 4), ('s', 4)] — top N by count
c["z"] # 0 — missing key, no KeyError
c.update("pp") # add more counts -> p becomes 4
Counter([1, 1, 2]) + Counter([1, 3]) # Counter({1: 3, 2: 1, 3: 1})
It replaces the manual d[x] = d.get(x, 0) + 1 pattern and supports arithmetic
between counters. Reach for Counter whenever the task is "how many of each?" —
word frequencies, tallies, or finding the most common elements.
defaultdict(factory) is a dict that auto-creates a missing key's value
by calling the zero-argument factory (like list, int, or set) the first
time you access it — so you can append or increment without initializing.
from collections import defaultdict
groups = defaultdict(list)
groups["fruits"].append("apple") # key auto-created as [] then appended
# {'fruits': ['apple']}
counts = defaultdict(int)
for ch in "aab":
counts[ch] += 1 # missing keys start at 0
# {'a': 2, 'b': 1}
The difference from setdefault: setdefault evaluates its default
on every call even when the key exists (wasteful), whereas defaultdict
only calls the factory on a miss. Use defaultdict for grouping and
counting loops; use plain setdefault for a one-off default insertion.
A deque ("double-ended queue") gives O(1) appends and pops at both
ends, whereas a list is O(n) for operations at the front (every element
shifts). It also supports a maxlen for a fixed-size sliding window.
from collections import deque
d = deque([1, 2, 3])
d.appendleft(0) # O(1) — list.insert(0, x) would be O(n)
d.append(4) # O(1)
d.popleft() # O(1) — efficient FIFO queue
window = deque(maxlen=3)
for x in [1, 2, 3, 4]:
window.append(x) # auto-drops from the left
# deque([2, 3, 4], maxlen=3)
Use a deque for queues, BFS, and sliding windows; with maxlen it
auto-discards the oldest item when full (great for "last N" buffers). Stick with
a list when you only push/pop at the end or need fast random indexing.
Mostly not for ordering itself — since Python 3.7 a plain dict already
preserves insertion order, so OrderedDict is no longer needed just to remember
order. But it still has a couple of distinct features a regular dict lacks.
from collections import OrderedDict
# 1) order-sensitive equality
OrderedDict(a=1, b=2) == OrderedDict(b=2, a=1) # False
dict(a=1, b=2) == dict(b=2, a=1) # True (order ignored)
# 2) move_to_end and a popitem(last=...) toggle
od = OrderedDict(a=1, b=2, c=3)
od.move_to_end("a") # OrderedDict([('b',2),('c',3),('a',1)])
od.popitem(last=False) # pop from the FRONT — FIFO
So use OrderedDict when you need order-sensitive ==, move_to_end,
or popitem(last=False) — for example, building an LRU cache. For plain
"keep the order I inserted," a regular dict is now enough.
ChainMap groups multiple dicts into a single, layered view without
copying them. Lookups search the underlying mappings in order and return the
first match, so earlier maps shadow later ones.
from collections import ChainMap
defaults = {"color": "red", "size": "M"}
overrides = {"color": "blue"}
settings = ChainMap(overrides, defaults)
settings["color"] # 'blue' — first map wins
settings["size"] # 'M' — falls through to defaults
settings["size"] = "L" # writes go to the FIRST map (overrides) only
It's perfect for layered configuration (CLI args over env vars over defaults) and scope-like lookups, because it stays live — changing a source dict shows up immediately. Note that writes and deletes only affect the first mapping. Use it to merge config layers without flattening them into one dict.
collections.namedtuple is a factory that creates an immutable tuple
subclass with named fields — lightweight, hashable records that read like
objects but behave like tuples (indexing, unpacking, comparison).
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
p.x, p[0] # 3, 3 — named and positional access
p._asdict() # {'x': 3, 'y': 4}
p._replace(x=9) # Point(x=9, y=4) — returns a NEW tuple (immutable)
It rounds out the module's record-like tools alongside Counter,
defaultdict, and deque. For richer needs — type hints, defaults, methods —
typing.NamedTuple or a @dataclass is the modern choice. Use namedtuple for
simple, immutable, self-documenting records.
Practice tests are coming soon
Get notified when interactive mock interviews and quizzes launch.