The Python collections module provides specialized container types beyond the built-in dict, list, tuple, and set. These tools make common tasks such as counting, grouping, queue handling, and lightweight records clearer.
The official Python documentation covers the full collections module. Related PythonPool guides cover incrementing dictionary values, lookup tables, BFS in Python, and queue peek.
Use collections when a purpose-built container makes the intent obvious. A plain dictionary can do many of these jobs, but Counter, defaultdict, and deque communicate the intended behavior directly.
The module is part of the standard library, so these containers are available without third-party packages. That makes them good choices for scripts, interviews, production services, and teaching examples where portability matters.
Choose the tool based on the operation you repeat most often. Counting, grouping, queueing, and layering mappings each have a dedicated container. Using the dedicated container often removes setup code and reduces the chance of edge-case mistakes.
These containers also make code reviews easier. A reviewer can see Counter and immediately understand that counts are central to the code. They can see deque and expect queue-style access from both ends.
Count Items With Counter
Counter counts hashable items and stores each item with its frequency.
from collections import Counter
words = ["red", "blue", "red", "green", "blue", "red"]
counts = Counter(words)
print(counts)
print(counts.most_common(2))
most_common() returns the highest counts first.
Use Counter for word counts, category counts, label frequencies, event summaries, and any task where the main output is a count per item.
A Counter behaves like a dictionary in many ways, but it understands count-specific operations. You can update it with more data, ask for the most common items, and combine counters when comparing two groups.
Group Values With defaultdict
defaultdict creates a default value when a missing key is accessed.
from collections import defaultdict
rows = [
("fruit", "apple"),
("fruit", "banana"),
("color", "blue"),
]
groups = defaultdict(list)
for category, item in rows:
groups[category].append(item)
print(dict(groups))
The list factory creates a new empty list for each new key.
This is cleaner than checking whether each key exists before appending. Convert the result to a regular dictionary before returning JSON or displaying it outside the function.
defaultdict is not limited to lists. Use defaultdict(int) for counters, defaultdict(set) for unique grouped values, and a custom factory when each key needs a more complex starting object.
Use deque As A Queue
deque supports efficient appends and pops from both ends.
from collections import deque
queue = deque(["A"])
queue.append("B")
queue.append("C")
print(queue.popleft())
print(queue.popleft())
Use deque for queues, breadth-first search, sliding windows, and producer-consumer style workflows.
A list can remove from the front with pop(0), but that operation shifts the remaining items. deque.popleft() is designed for queue removal.
deque also supports appendleft(), pop(), and optional maximum lengths. A bounded deque is useful for keeping a recent history without manually trimming old entries.
Create Simple Records With namedtuple
namedtuple creates tuple-like records with named fields.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
point = Point(3, 4)
print(point.x)
print(point.y)
print(point[0])
Named tuples are immutable and lightweight.
Use them for small records when you want readable field access without writing a full class. For richer behavior, validation, or defaults, a dataclass may be a better fit.
Named tuples are especially useful when returning more than one value from a helper and you want call sites to read point.x instead of relying on numeric tuple positions.
Layer Mappings With ChainMap
ChainMap searches multiple mappings as one combined view.
from collections import ChainMap
defaults = {"theme": "light", "page_size": 20}
user_settings = {"page_size": 50}
settings = ChainMap(user_settings, defaults)
print(settings["theme"])
print(settings["page_size"])
The first mapping wins when the same key appears in more than one mapping.
This is useful for layered configuration, command-line overrides, environment settings, and defaults that should remain separate.
A ChainMap does not copy all entries into one dictionary. It keeps the mappings linked, so updates to the first mapping are visible through the combined view.
Reorder Items With OrderedDict
Modern dictionaries preserve insertion order, but OrderedDict still has order-focused methods such as move_to_end().
from collections import OrderedDict
recent = OrderedDict()
recent["home"] = 1
recent["docs"] = 2
recent["pricing"] = 3
recent.move_to_end("home")
print(list(recent))
move_to_end() is useful for recency tracking and cache-like logic.
For ordinary ordered mappings, a normal dictionary is enough in modern Python. Use OrderedDict when you specifically need its order manipulation features.
That distinction keeps code modern without losing specialized behavior. If you only need insertion order, use dict. If you need to move keys to either end, reach for OrderedDict.
In short, use Counter for counts, defaultdict for automatic groups or counters, deque for queues, namedtuple for lightweight records, ChainMap for layered mappings, and OrderedDict when order operations are part of the task.
Start with built-in containers when they are clear enough. Move to collections when the specialized behavior improves readability, removes repeated boilerplate, or makes performance characteristics more predictable.