Collections Module

Difficulty: Intermediate

The collections module in Python's standard library provides specialized container data types that extend the functionality of built-in dicts, lists, and tuples. These are purpose-built for common programming patterns and can make your code more concise, readable, and efficient. The most commonly used classes are defaultdict, Counter, OrderedDict, and ChainMap.

defaultdict is a subclass of dict that calls a factory function to supply missing values. Instead of raising KeyError when you access a missing key, it automatically creates the key with a default value produced by the factory. Common factories include int (defaults to 0, perfect for counting), list (defaults to empty list, perfect for grouping), and set (defaults to empty set, perfect for unique grouping). This eliminates the need for setdefault() or explicit key existence checks.

Counter is a specialized dict subclass for counting hashable objects. Pass it any iterable, and it creates a dictionary where keys are elements and values are their counts. Counter provides useful methods like most_common(n) to get the n most frequent elements, and supports arithmetic operations (+, -, &, |) between Counters. It is the go-to tool for frequency analysis, histogram building, and vote counting.

OrderedDict was historically important because regular dicts did not preserve insertion order before Python 3.7. While regular dicts now maintain insertion order, OrderedDict still has unique features: it supports move_to_end() to reposition keys, its equality comparison considers order (unlike regular dicts), and it provides a pop(last=True/False) method for LIFO/FIFO operations. It remains relevant in specific use cases like LRU cache implementations.

ChainMap groups multiple dictionaries into a single view. Lookups search through the maps in order, returning the first match found. This is ideal for implementing scoped configurations, layered settings (defaults -> user -> environment), or template variable resolution. Changes to a ChainMap write to the first map only, leaving the others untouched. The maps attribute provides access to the underlying list of dicts.

Code examples

defaultdict for counting and grouping

from collections import defaultdict

# Counting with defaultdict(int)
word_count = defaultdict(int)
for word in 'the cat sat on the mat the cat'.split():
    word_count[word] += 1
print('Word counts:', dict(word_count))

# Grouping with defaultdict(list)
students = [('A', 'Alice'), ('B', 'Bob'), ('A', 'Charlie'), ('C', 'Diana'), ('B', 'Eve')]
grade_groups = defaultdict(list)
for grade, name in students:
    grade_groups[grade].append(name)
print('Groups:', dict(grade_groups))

# Unique grouping with defaultdict(set)
tags = [('python', 'tutorial'), ('python', 'guide'), ('java', 'tutorial'), ('python', 'tutorial')]
tag_types = defaultdict(set)
for lang, content_type in tags:
    tag_types[lang].add(content_type)
print('Tags:', {k: sorted(v) for k, v in tag_types.items()})

defaultdict(int) starts missing keys at 0, perfect for counting. defaultdict(list) starts with empty lists for grouping. defaultdict(set) starts with empty sets for unique grouping. Convert to dict() for clean printing.

Counter for frequency analysis

from collections import Counter

# Count from a string
letters = Counter('abracadabra')
print('Letter counts:', letters)
print('Most common 3:', letters.most_common(3))

# Count from a list
colors = ['red', 'blue', 'red', 'green', 'blue', 'red']
color_count = Counter(colors)
print('\nColor counts:', color_count)
print('Red count:', color_count['red'])
print('Missing key:', color_count['yellow'])  # returns 0, not KeyError

# Arithmetic with Counters
shop1 = Counter(apple=3, banana=2)
shop2 = Counter(apple=1, banana=4, cherry=5)
combined = shop1 + shop2
print('\nCombined:', combined)

diff = shop2 - shop1
print('Difference:', diff)

Counter counts hashable objects from any iterable. most_common(n) returns the n most frequent as (element, count) tuples. Accessing a missing key returns 0 instead of raising KeyError. Counter arithmetic (+, -) combines or subtracts counts; negative counts are dropped by subtraction.

OrderedDict features

from collections import OrderedDict

# OrderedDict preserves insertion order (like regular dict in 3.7+)
od = OrderedDict()
od['banana'] = 3
od['apple'] = 1
od['cherry'] = 2
print('OrderedDict:', od)

# move_to_end()
od.move_to_end('banana')
print('After move banana to end:', od)
od.move_to_end('cherry', last=False)  # move to beginning
print('After move cherry to start:', od)

# OrderedDict equality cares about order
od1 = OrderedDict([('a', 1), ('b', 2)])
od2 = OrderedDict([('b', 2), ('a', 1)])
print('\nOrderedDicts equal:', od1 == od2)

# Regular dicts ignore order in equality
d1 = {'a': 1, 'b': 2}
d2 = {'b': 2, 'a': 1}
print('Regular dicts equal:', d1 == d2)

OrderedDict's key advantage over regular dicts is move_to_end() for reordering, and order-sensitive equality comparison. Regular dict equality ignores order. OrderedDict is useful for LRU caches and ordered configurations.

ChainMap for layered lookups

from collections import ChainMap

# Layered configuration
defaults = {'color': 'blue', 'size': 'medium', 'debug': False}
user_settings = {'color': 'red', 'font': 'Arial'}
env_overrides = {'debug': True}

config = ChainMap(env_overrides, user_settings, defaults)
print('color:', config['color'])       # from user_settings
print('size:', config['size'])         # from defaults
print('debug:', config['debug'])       # from env_overrides
print('font:', config['font'])         # from user_settings

# Keys from all maps
print('\nAll keys:', sorted(config.keys()))

# New child scope
local = config.new_child({'color': 'green', 'temp': True})
print('\nLocal color:', local['color'])
print('Local debug:', local['debug'])
print('Maps count:', len(local.maps))

ChainMap searches maps in order, returning the first match. This creates a layered lookup: env_overrides -> user_settings -> defaults. new_child() adds a new scope on top. Writes only affect the first map, preserving the rest.

Key points

Concepts covered

defaultdict, Counter, OrderedDict, ChainMap, collections module