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.