Python Tutorial
The collections Module
The collections module provides specialised containers that make common tasks shorter and faster than with plain dicts and lists: counting things, grouping items, fixed records with names, double-ended queues, chaining configuration layers, and more.
This lesson covers Counter, defaultdict, OrderedDict, deque, namedtuple, ChainMap, and the UserDict/UserList base classes, each with a practical example.
Counter
Counter(iterable) counts hashable items. most_common(n) returns the top items, missing keys count as 0, and counters support +, -, & and |. Perfect for word frequencies, votes, and inventory.
defaultdict
defaultdict(factory) creates a missing key's value automatically by calling the factory: defaultdict(list) for grouping, defaultdict(int) for counting, defaultdict(set) for unique grouping. No more if key not in d checks.
OrderedDict, deque, namedtuple, ChainMap
OrderedDict remembers order (like dict) and adds move_to_end() and order-sensitive equality — handy for LRU caches. deque is the double-ended queue. namedtuple makes tuple records with field names. ChainMap searches several dicts in order — e.g. command-line options, then environment, then defaults — without copying them.
UserDict and UserList
To create your own dict-like or list-like class, subclass UserDict or UserList: they route all operations through your overridden methods, which is more reliable than subclassing the built-in dict directly.
Examples
Counter for frequencies
from collections import Counter
text = "the quick brown fox jumps over the lazy dog the end"
words = Counter(text.split())
print(words.most_common(2))
print(words["the"], words["cat"])
votes = Counter(["python", "java", "python", "go", "python", "java"])
print(votes)
stock = Counter(pens=10, books=4)
sold = Counter(pens=3, books=4)
print(stock - sold, sorted((stock + sold).elements())[:3])
[('the', 3), ('quick', 1)]
3 0
Counter({'python': 3, 'java': 2, 'go': 1})
Counter({'pens': 7}) ['books', 'books', 'books']
defaultdict for grouping and counting
from collections import defaultdict
orders = [("Asha", "pen"), ("Ravi", "book"), ("Asha", "bag"), ("Asha", "pen")]
by_customer = defaultdict(list)
unique_items = defaultdict(set)
counts = defaultdict(int)
for customer, item in orders:
by_customer[customer].append(item)
unique_items[customer].add(item)
counts[customer] += 1
print(dict(by_customer))
print({k: sorted(v) for k, v in unique_items.items()})
print(dict(counts))
{'Asha': ['pen', 'bag', 'pen'], 'Ravi': ['book']}
{'Asha': ['bag', 'pen'], 'Ravi': ['book']}
{'Asha': 3, 'Ravi': 1}
OrderedDict as an LRU cache, and ChainMap for layered settings
from collections import OrderedDict, ChainMap
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.data = OrderedDict()
def get(self, key):
if key not in self.data:
return None
self.data.move_to_end(key)
return self.data[key]
def put(self, key, value):
self.data[key] = value
self.data.move_to_end(key)
if len(self.data) > self.capacity:
self.data.popitem(last=False) # evict least recently used
cache = LRUCache(2)
cache.put("a", 1); cache.put("b", 2); cache.get("a"); cache.put("c", 3)
print(list(cache.data))
defaults = {"theme": "light", "language": "en", "page_size": 20}
env = {"language": "hi"}
cli = {"page_size": 50}
settings = ChainMap(cli, env, defaults)
print(settings["theme"], settings["language"], settings["page_size"])
['a', 'c']
light hi 50
UserDict: a dictionary with case-insensitive keys
from collections import UserDict
class CaseInsensitiveDict(UserDict):
def __setitem__(self, key, value):
super().__setitem__(key.lower(), value)
def __getitem__(self, key):
return super().__getitem__(key.lower())
headers = CaseInsensitiveDict()
headers["Content-Type"] = "application/json"
print(headers["content-type"], headers["CONTENT-TYPE"], dict(headers))
application/json application/json {'content-type': 'application/json'}
Common Mistakes
- Writing manual counting loops instead of Counter.
- Checking "if key not in d: d[key] = []" everywhere instead of defaultdict(list).
- Printing a defaultdict and being surprised that simply reading a missing key created it.
- Subclassing dict and overriding __setitem__, which update() and the constructor bypass; use UserDict.
Key Points to Remember
- Counter counts items and finds the most common.
- defaultdict creates missing values automatically (list, int, set...).
- OrderedDict adds move_to_end/popitem(last=False) for LRU-style logic.
- deque, namedtuple and ChainMap cover queues, records and layered lookups.
- UserDict/UserList are the safe bases for custom containers.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.