The collections module

Part of the free LogicWiz Python course, Module 12: Iteration and the standard toolkit. The examples and practice run in the app.

"Which dish sold the most today?" Aarav writes a loop with a dict and if item in counts. Meher types Counter(orders).most_common(1). Same answer, one line.

You'll learn to

  • Count with `Counter` and find the top items with `most_common`
  • Group and total with `defaultdict`
  • Use `deque` for fast queues and `namedtuple` for readable records
  • Know when `OrderedDict` still matters, and when each tool beats a plain dict or list

Learn

Counter: counting made easy

from collections import Counter

orders = ["chai", "samosa", "chai", "vada pav", "chai", "samosa"]
c = Counter(orders)
print(c)
print(c["chai"], c["dosa"])        # missing items count as 0
print(c.most_common(2))
c.update(["dosa", "chai"])
print(c["chai"], c["dosa"])
print(Counter("mississippi").most_common(1))

defaultdict: no more "if key in dict"

from collections import defaultdict

by_table = defaultdict(list)
for table, item in [(1, "chai"), (2, "dosa"), (1, "vada")]:
    by_table[table].append(item)
print(dict(by_table))

spent = defaultdict(int)
for name, amount in [("Riya", 40), ("Kabir", 25), ("Riya", 15)]:
    spent[name] += amount
print(dict(spent))

A missing key is created on first use with list() (an empty list) or int() (0).

deque: a queue with two fast ends

from collections import deque

queue = deque(["Riya", "Kabir"])
queue.append("Sneha")            # joins at the back
queue.appendleft("Teacher")      # jumps the queue
print(queue.popleft())           # served first
print(queue)

recent = deque(maxlen=3)         # keeps only the last 3
for token in ["A1", "A2", "A3", "A4"]:
    recent.append(token)
print(list(recent))

list.pop(0) shifts every other item, so it is O(n). deque.popleft() is O(1).

namedtuple: a tuple with field names

from collections import namedtuple

Item = namedtuple("Item", ["name", "price", "veg"])
s = Item("samosa", 15, True)

print(s.name, s.price)
print(s)
name, price, veg = s              # still unpacks like a tuple
print(s._replace(price=18))       # a new tuple; s is unchanged

OrderedDict: reorder on purpose

from collections import OrderedDict

recent = OrderedDict()
for page in ["menu", "cart", "offers"]:
    recent[page] = True
recent.move_to_end("menu")           # used again: now newest
print(list(recent))
print(recent.popitem(last=False))    # remove the oldest

Since Python 3.7 a plain dict keeps insertion order too. OrderedDict adds move_to_end and popitem(last=False), perfect for an LRU cache.

Which tool when?

NeedReach for
Count things, find the top kCounter + mostcommon(k)
Group items or total by keydefaultdict(list) / defaultdict(int)
Queue, add/remove at both ends, last n itemsdeque (with maxlen)
Small fixed record, read-onlynamedtuple
Move keys to the end, pop the oldestOrderedDict

defaultdict creates a key just by looking it up. if d['dosa'] == 0: adds 'dosa' to the dict. Use 'dosa' in d or d.get('dosa') to check without adding. A Counter returns 0 for a missing key without adding it.

Interview favourites: "check if two strings are anagrams" (Counter(a) == Counter(b)), "top k frequent elements" (most_common(k)), "first non-repeating character" (Counter + one pass), and "design an LRU cache" (OrderedDict). You'll write all four below.

In one line

Counter counts (most_common(k)), defaultdict groups and totals, deque gives O(1) ends (with maxlen), namedtuple names tuple fields, OrderedDict adds move_to_end.

Interview corner

How do you find the k most frequent elements in a list?

Counter(items).most_common(k) gives the top k (item, count) pairs in O(n log k). Ties keep first-seen order. Without the library: count with a dict, then heapq.nlargest(k, counts, key=counts.get).

What is the difference between dict.get(), setdefault() and defaultdict?

d.get(k, 0) reads with a fallback and never adds the key. d.setdefault(k, []) returns the value, inserting the default if missing. defaultdict(list) inserts the default automatically on any missing lookup, which is perfect for grouping but can surprise you when you only meant to check.

Why use a deque instead of a list for a queue?

A list is a dynamic array: pop(0) and insert(0, x) shift every element, O(n). A deque is a doubly linked block structure with O(1) append, appendleft, pop and popleft. deque(maxlen=n) also gives a ready-made "last n items" buffer. Indexing the middle of a deque is slower than a list.

Do we still need OrderedDict now that dicts keep order?

Plain dicts keep insertion order since Python 3.7, so usually no. OrderedDict still offers move_to_end() and popitem(last=False), which make an LRU cache easy, and its == checks order (two plain dicts with the same items in different order are equal).