Overview
collections provides containers that replace common hand-written dict and list patterns. Typed record alternatives are in python-typing. Import what you need:
from collections import Counter, defaultdict, namedtuple, deque, ChainMap, OrderedDictCounter
Use Counter for tallies and top-N queries.
| Pattern | Code | Result |
|---|---|---|
| Count items | c = Counter("aababc") | {'a': 3, 'b': 2, 'c': 1} |
| Top N | c.most_common(2) | [('a', 3), ('b', 2)] |
| Missing key | c["z"] | 0, not KeyError |
| Add / subtract | c + d, c - d | New Counter; keeps positive counts only. |
| In-place subtract | c.subtract("aab") | Keeps zero and negative counts. |
| Expand / sum | list(c.elements()), c.total() | Repeated elements; total count (3.10+). |
defaultdict
Use defaultdict to group or count without existence checks.
| Pattern | Code |
|---|---|
| Group into lists | d = defaultdict(list); d[k].append(v) |
| Count | d = defaultdict(int); d[k] += 1 |
| Nested | d = defaultdict(lambda: defaultdict(int)) |
| Custom default | defaultdict(lambda: "N/A"); the factory is any zero-argument callable. |
| Read without inserting | d.get(k) or k in d |
namedtuple and NamedTuple
Use a dataclass when you need mutability or methods.
from typing import NamedTuple
class Config(NamedTuple):
host: str
port: int = 5432
cfg = Config("localhost")
cfg._replace(port=6543) # new instance
cfg._asdict() # {'host': 'localhost', 'port': 5432}namedtuple("P", ["x", "y"], defaults=[0, 0]) is the untyped form.
deque
Use deque for queues and sliding windows.
| Operation | Code |
|---|---|
| Bounded buffer | deque(maxlen=100) drops the oldest item when full. |
| Both ends in O(1) | append, appendleft, pop, popleft |
| Rotate | d.rotate(1) right, negative left. |
| Extend left | d.extendleft([1, 2]) adds in reverse order. |
ChainMap
Lookups search the maps in order; writes go to the first map only.
config = ChainMap(cli_args, env_vars, defaults) # cli_args wins
config.new_child({"debug": True}) # push a layer
config.parents # drop the first layer
config.maps # underlying dicts in priority orderOrderedDict
dict preserves insertion order since Python 3.7. Use OrderedDict only for move_to_end(), popitem(last=False), or equality that depends on order.
Gotchas
Avoid these; each loses data or inserts keys silently.
| Gotcha | Fix |
|---|---|
c - d silently drops zero and negative counts. | Use subtract() when you need them. |
d[missing] on a defaultdict inserts the default. | Use d.get(k) or k in d to probe. |
A full deque(maxlen=n) discards data silently on append. | Check len(d) == d.maxlen first if loss matters. |
ChainMap writes never reach lower maps. | Update the underlying dict directly. |
Positional construction of a namedtuple breaks when fields are reordered. | Construct with keywords. |