Python collections — Counter, defaultdict, namedtuple, deque
The collectionscollections module provides specialized container types that go beyond the built-in listlist, dictdict, tupletuple, and setset. They make common tasks — counting, grouping, fixed-field records, and fast queues — shorter and faster.
from collections import Counter, defaultdict, namedtuple, deque
print(Counter("banana")) # Counter({'a': 3, 'n': 2, 'b': 1})
print(defaultdict(list)) # defaultdict(<class 'list'>, {})
Point = namedtuple("Point", "x y")
print(Point(1, 2)) # Point(x=1, y=2)
print(deque([1, 2, 3])) # deque([1, 2, 3])from collections import Counter, defaultdict, namedtuple, deque
print(Counter("banana")) # Counter({'a': 3, 'n': 2, 'b': 1})
print(defaultdict(list)) # defaultdict(<class 'list'>, {})
Point = namedtuple("Point", "x y")
print(Point(1, 2)) # Point(x=1, y=2)
print(deque([1, 2, 3])) # deque([1, 2, 3])| Type | Use it for |
|---|---|
CounterCounter | Counting hashable items (tallies, frequencies). |
defaultdictdefaultdict | A dict that auto-creates default values for missing keys. |
namedtuplenamedtuple | Lightweight, immutable records with named fields. |
dequedeque | Fast appends/pops at both ends (queues, stacks). |
OrderedDictOrderedDict | A dict with extra order-related methods. |
ChainMapChainMap | Search multiple dicts as one. |
Counter
CounterCounter is a dictdict subclass for counting. Pass any iterable and it tallies occurrences.
from collections import Counter
votes = ["a", "b", "a", "c", "a", "b"]
c = Counter(votes)
print(c) # Counter({'a': 3, 'b': 2, 'c': 1})
print(c["a"]) # 3
print(c["z"]) # 0 (missing keys return 0, never KeyError)
# Most common items
print(c.most_common(2)) # [('a', 3), ('b', 2)]
# Counters do arithmetic
c2 = Counter("aab")
print(c + c2) # combine counts
print(c - c2) # subtract counts
# Update with more data
c.update(["a", "d"])
print(c["a"], c["d"]) # 4 1from collections import Counter
votes = ["a", "b", "a", "c", "a", "b"]
c = Counter(votes)
print(c) # Counter({'a': 3, 'b': 2, 'c': 1})
print(c["a"]) # 3
print(c["z"]) # 0 (missing keys return 0, never KeyError)
# Most common items
print(c.most_common(2)) # [('a', 3), ('b', 2)]
# Counters do arithmetic
c2 = Counter("aab")
print(c + c2) # combine counts
print(c - c2) # subtract counts
# Update with more data
c.update(["a", "d"])
print(c["a"], c["d"]) # 4 1| Method | Returns |
|---|---|
c.most_common(n)c.most_common(n) | The nn highest-count (item, count)(item, count) pairs. |
c.elements()c.elements() | An iterator repeating each element by its count. |
c.update(iterable)c.update(iterable) | Add counts from another iterable/Counter. |
c.total()c.total() | Sum of all counts (Python 3.10+). |
defaultdict
A regular dict raises KeyErrorKeyError for a missing key. A defaultdictdefaultdict instead calls a factory function to create a default value automatically — perfect for grouping and accumulating.
from collections import defaultdict
# Group words by their first letter
words = ["apple", "banana", "avocado", "cherry", "blueberry"]
groups = defaultdict(list)
for w in words:
groups[w[0]].append(w)
print(dict(groups))
# {'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}
# Count with an int factory (default 0)
counts = defaultdict(int)
for ch in "mississippi":
counts[ch] += 1
print(dict(counts)) # {'m': 1, 'i': 4, 's': 4, 'p': 2}from collections import defaultdict
# Group words by their first letter
words = ["apple", "banana", "avocado", "cherry", "blueberry"]
groups = defaultdict(list)
for w in words:
groups[w[0]].append(w)
print(dict(groups))
# {'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}
# Count with an int factory (default 0)
counts = defaultdict(int)
for ch in "mississippi":
counts[ch] += 1
print(dict(counts)) # {'m': 1, 'i': 4, 's': 4, 'p': 2}The factory can be listlist, intint, setset, dictdict, or any zero-argument callable.
namedtuple
namedtuplenamedtuple creates a tuple subclass with named fields — readable, immutable records without writing a full class.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y) # 3 4 (access by name)
print(p[0], p[1]) # 3 4 (still a tuple — index access works)
# Unpacking works too
x, y = p
print(x, y) # 3 4
# Handy helpers
print(p._asdict()) # {'x': 3, 'y': 4}
p2 = p._replace(y=10) # returns a NEW point (immutable)
print(p2) # Point(x=3, y=10)from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y) # 3 4 (access by name)
print(p[0], p[1]) # 3 4 (still a tuple — index access works)
# Unpacking works too
x, y = p
print(x, y) # 3 4
# Handy helpers
print(p._asdict()) # {'x': 3, 'y': 4}
p2 = p._replace(y=10) # returns a NEW point (immutable)
print(p2) # Point(x=3, y=10)Use a namedtuple when you have a small fixed set of fields and want clarity over a bare tuple or dict.
deque
A dequedeque (“double-ended queue”) supports O(1) appends and pops from both ends — unlike a list, which is slow at the front.
from collections import deque
dq = deque([1, 2, 3])
dq.append(4) # add right -> deque([1, 2, 3, 4])
dq.appendleft(0) # add left -> deque([0, 1, 2, 3, 4])
print(dq.pop()) # 4 (remove right)
print(dq.popleft()) # 0 (remove left)
print(dq) # deque([1, 2, 3])
# Fixed-size deque: old items drop off automatically
last3 = deque(maxlen=3)
for i in range(6):
last3.append(i)
print(last3) # deque([3, 4, 5], maxlen=3)
# Rotate
d = deque([1, 2, 3, 4])
d.rotate(1) # deque([4, 1, 2, 3])
print(d)from collections import deque
dq = deque([1, 2, 3])
dq.append(4) # add right -> deque([1, 2, 3, 4])
dq.appendleft(0) # add left -> deque([0, 1, 2, 3, 4])
print(dq.pop()) # 4 (remove right)
print(dq.popleft()) # 0 (remove left)
print(dq) # deque([1, 2, 3])
# Fixed-size deque: old items drop off automatically
last3 = deque(maxlen=3)
for i in range(6):
last3.append(i)
print(last3) # deque([3, 4, 5], maxlen=3)
# Rotate
d = deque([1, 2, 3, 4])
d.rotate(1) # deque([4, 1, 2, 3])
print(d)| Method | Effect |
|---|---|
append(x)append(x) / appendleft(x)appendleft(x) | Add to the right / left. |
pop()pop() / popleft()popleft() | Remove from the right / left. |
extend(it)extend(it) / extendleft(it)extendleft(it) | Add many to the right / left. |
rotate(n)rotate(n) | Rotate items nn steps to the right. |
maxlenmaxlen | Optional fixed length; overflow drops the opposite end. |
OrderedDict and ChainMap (briefly)
from collections import OrderedDict, ChainMap
# OrderedDict: insertion order is preserved (regular dicts do this too since 3.7),
# but it adds move_to_end() and an order-sensitive ==.
od = OrderedDict([("a", 1), ("b", 2)])
od.move_to_end("a")
print(list(od)) # ['b', 'a']
# ChainMap: search several dicts as one, first match wins.
defaults = {"color": "red", "size": "M"}
overrides = {"color": "blue"}
settings = ChainMap(overrides, defaults)
print(settings["color"]) # blue (overrides win)
print(settings["size"]) # M (falls back to defaults)from collections import OrderedDict, ChainMap
# OrderedDict: insertion order is preserved (regular dicts do this too since 3.7),
# but it adds move_to_end() and an order-sensitive ==.
od = OrderedDict([("a", 1), ("b", 2)])
od.move_to_end("a")
print(list(od)) # ['b', 'a']
# ChainMap: search several dicts as one, first match wins.
defaults = {"color": "red", "size": "M"}
overrides = {"color": "blue"}
settings = ChainMap(overrides, defaults)
print(settings["color"]) # blue (overrides win)
print(settings["size"]) # M (falls back to defaults)Common pitfalls
defaultdictdefaultdictcreates keys on access — even just readingd[missing]d[missing]inserts it. Used.get(k)d.get(k)if you don’t want that.- namedtuples are immutable —
_replace_replacereturns a new instance; it doesn’t mutate. - Use
dequedequefor queues, notlist.pop(0)list.pop(0), which is O(n). CounterCountervalues can go negative after subtraction; use+c+cto drop non-positive counts.
Practice Exercises
Exercise 1 – Count characters
Exercise 2 – Group numbers by even/odd
Exercise 3 – Keep the last 3 items
Summary
CounterCountertallies items and ranks them withmost_commonmost_common.defaultdictdefaultdictremovesKeyErrorKeyErrorboilerplate for grouping/accumulating.namedtuplenamedtuplegives readable, immutable records with named fields.dequedequeis the right tool for fast double-ended queues and bounded buffers.OrderedDictOrderedDictandChainMapChainMapcover order-sensitive and layered-lookup needs.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
