Advanced Container Utilities in Python's collections Module
The collections module provides specialized container data types that serve as alternatives to Python's general-purpose built-in containers like dict, list, set, and tuple.
Available Classes
- ChainMap: Groups multiple mappings into a single view.
- Counter: Subclass of
dictfor counting hashable objects. - deque: List-like container with fast appends and pops on both ends.
- defaultdict: Dictionary subclass that calls a factory function to supply missing values.
- namedtuple(): Factory function for creating tuple subclasses with named fields.
- OrderedDict: Dictionary that remembers insertion order.
- UserDict: Wrapper around dictionary objects to ease subclassing.
- UserList: Wrapper around list objects to ease subclassing.
- UserString: Wrapper around string objects to ease subclassing.
ChainMap
ChainMap groups multiple dictionaries or mapipngs into a single unit. It is often faster than creating a new dictionary and calling update() multiple times.
from collections import ChainMap
info_a = {'x': 10, 'y': 20}
info_b = {'y': 30, 'z': 40}
merged = ChainMap(info_a, info_b)
reversed_merged = ChainMap(info_b, info_a)
print(merged)
print(reversed_merged)
# Lookups scan the first map, then the second
for key, val in merged.items():
print(key, val)
print('---')
for key, val in reversed_merged.items():
print(key, val)
Output:
ChainMap({'x': 10, 'y': 20}, {'y': 30, 'z': 40})
ChainMap({'y': 30, 'z': 40}, {'x': 10, 'y': 20})
x 10
y 20
z 40
---
y 30
z 40
x 10
Updates affect only the first mapping in the chain:
def demo_chainmap():
from collections import ChainMap
base = {'id': 101}
details = {'role': 'admin'}
prefs = {'theme': 'dark'}
lookup = ChainMap(base, details, prefs)
print(lookup)
print(lookup['role'], lookup.maps)
lookup.update({'role': 'user'})
print(lookup)
# Modifying original dictionaries reflects in the ChainMap
details['role'] = 'editor'
prefs['theme'] = 'light'
print(lookup)
# Update a specific map in the chain
lookup.maps[0]['id'] = 202
lookup.maps[1]['role'] = 'viewer'
print(lookup)
print('---')
m1 = {'key': 'A'}
m2 = {'key': 'B'}
cm = ChainMap(m1, m2)
print(cm['key'])
demo_chainmap()
Counter
Counter is a specialized dictionary for tallying occurrences of items.
Key methods:
elements(): Returns an iterator over elements repeaitng as many times as their count.most_common([n]): Returns a list of thenmost common elements and their counts.subtract([iterable-or-mapping]): Subtracts counts.update([iterable-or-mapping]): Adds counts.
from collections import Counter
# Character frequency
print(Counter('banana'))
# Word frequency
text = 'cat dog cat bird dog dog'
word_counts = Counter(text.split())
print(word_counts)
# Manual tally
tally = Counter()
for item in ['book', 'pen', 'book', 'pen', 'pen']:
tally[item] += 1
print(tally)
# Access counts
print(word_counts['dog'])
print(list(word_counts.elements()))
# Update counts
c1 = Counter('apple apple orange'.split())
c2 = Counter('orange banana'.split())
print(c1 + c2)
c1.update(c2)
print(c1)
# Subtract
c1.subtract(Counter('apple'.split()))
print(c1)
# Clear
c1.clear()
print(c1)
deque
deque (double-ended queue) supports O(1) appends and pops from both ends, making it ideal for queue/stack implementations.
Common methods include append(), appendleft(), pop(), popleft(), extend(), extendleft(), rotate(), and maxlen for fixed-size buffers.
from collections import deque
queue = deque(maxlen=10)
queue.extend([1, 2, 3])
print(queue)
queue.append(4)
queue.appendleft(0)
print(queue)
queue.extendleft([-2, -1]) # Note: order reverses
print(queue)
print(queue.count(3))
queue.rotate(1)
print(queue)
queue.reverse()
print(queue)
defaultdict
defaultdict provides a default value for missing keys using a factory function (e.g., list, int, set).
from collections import defaultdict
# Grouping with list
records = [('a', 1), ('b', 2), ('a', 3)]
grouped = defaultdict(list)
for k, v in records:
grouped[k].append(v)
print(sorted(grouped.items()))
# Counting with int
word = 'banana'
char_count = defaultdict(int)
for ch in word:
char_count[ch] += 1
print(sorted(char_count.items()))
namedtuple
namedtuple creates lightweight object types with named fields.
from collections import namedtuple
Employee = namedtuple('Employee', ['id', 'name', 'dept'])
emp1 = Employee(101, 'Alice', 'Engineering')
print(emp1)
print(emp1.name)
print(emp1[0])
OrderedDict
OrderedDict maintains insertion order and provides order-sensitive operations.
from collections import OrderedDict
od = OrderedDict()
od['first'] = 100
od['second'] = 200
od['third'] = 300
print(od)
od.move_to_end('first')
print(od)
# Pop the last item
print(od.popitem())
print(od)
UserDict, UserList, UserString
These wrappers simplify subclassing by exposing the underlying container via an attribute:
UserDict: Wraps a dictionary; access the original viadata.UserList: Wraps a list; access the original viadata.UserString: Wraps a string; access the original viadata.
These are useful when you need to customize container behavior without dealing with the complexities of directly subclassing built-in types.