Python collections Cheat Sheet
1 · The lesson
readThe collections module gives you ready-made containers for jobs that plain lists and dicts make awkward: counting things, grouping them, keeping a queue, and storing small records. This page is for readers who already know lists and dicts and want the short version with examples you can run. For the full tutorial, see Collections.
1. Counter: count things
A Counter is a dict that maps each item to how many times it appears. Give it a list, a string or any other sequence.
from collections import Counter words = "the cat and the hat and the bat".split() counts = Counter(words) print(counts["the"]) # 3 print(counts.most_common(2)) # [('the', 3), ('and', 2)] print(Counter("banana")) # Counter({'a': 3, 'n': 2, 'b': 1}) print(counts.total()) # 8
most_common(n) gives the n most frequent items, biggest first. total() adds up all the counts (Python 3.10 and later).
2. Counter: add and subtract
You can add two counters to combine them, or subtract one from another. Here are a café's sales on two days.
from collections import Counter mon = Counter(tea=3, coffee=1) tue = Counter(tea=1, cake=2) print(mon + tue) # Counter({'tea': 4, 'cake': 2, 'coffee': 1}) print(mon - tue) # Counter({'tea': 2, 'coffee': 1}) mon.update(["tea", "tea"]) print(mon["tea"]) # 5
With -, any count that ends at 0 or below is dropped. Counter.update() adds to the counts. A plain dict.update() would replace them.
3. defaultdict: group and total
With a normal dict, reading a missing key raises KeyError. A defaultdict gives a missing key a starting value instead. You choose the starting value when you create it: list gives [], int gives 0, set gives an empty set.
from collections import defaultdict orders = [("Ada", 12), ("Linus", 5), ("Ada", 30)] by_customer = defaultdict(list) for name, price in orders: by_customer[name].append(price) print(dict(by_customer)) # {'Ada': [12, 30], 'Linus': [5]} spent = defaultdict(int) for name, price in orders: spent[name] += price print(dict(spent)) # {'Ada': 42, 'Linus': 5} tags = defaultdict(set) tags["Ada"].add("vip") tags["Ada"].add("vip") print(dict(tags)) # {'Ada': {'vip'}}
Without defaultdict, you would first check if name not in by_customer: and create the empty list yourself.
4. deque: a queue with two fast ends
A deque (say "deck") is a list-like sequence that is fast to add to and remove from at both ends. list.pop(0) has to move every other item, so a deque is the better choice for a queue.
from collections import deque q = deque(["Ada", "Grace"]) q.append("Linus") # join at the right end q.appendleft("Hedy") # join at the left end print(q.popleft()) # Hedy print(q.pop()) # Linus print(q) # deque(['Ada', 'Grace'])
5. deque: last N items, rotate
Give a deque a maxlen and it keeps only the newest items. When the deque is full, adding an item drops the oldest one. A deque with maxlen is a simple way to keep "recent searches". rotate(1) moves every item one place to the right, and the last item goes to the front.
from collections import deque recent = deque(maxlen=3) for search in ["tea", "jam", "figs", "milk"]: recent.append(search) print(list(recent)) # ['jam', 'figs', 'milk'] d = deque([1, 2, 3, 4]) d.rotate(1) print(d) # deque([4, 1, 2, 3]) d.rotate(-1) print(d) # deque([1, 2, 3, 4])
6. namedtuple: a tuple with field names
A namedtuple is a tuple whose items also have names. Use it for small records, such as a product with a name and a price, when you want p.price instead of p[1].
from collections import namedtuple Product = namedtuple("Product", ["name", "price"]) p = Product("tea", 4.5) print(p.price) # 4.5 print(p[0]) # tea name, price = p print(p) # Product(name='tea', price=4.5) print(p._replace(price=5.0)) # Product(name='tea', price=5.0) print(p._asdict()) # {'name': 'tea', 'price': 4.5}
_replace makes a changed copy. The original p stays the same, because tuples cannot change.
7. OrderedDict
Since Python 3.7, a plain dict keeps keys in the order you added them, so you rarely need OrderedDict. The main reason to use it is move_to_end.
from collections import OrderedDict pages = OrderedDict(home=1, shop=2, cart=3) pages.move_to_end("home") print(list(pages)) # ['shop', 'cart', 'home'] pages.move_to_end("cart", last=False) print(list(pages)) # ['cart', 'shop', 'home']
8. ChainMap: settings with defaults
A ChainMap looks up a key in several dicts, in order, and uses the first one that has it. This suits user settings: when the user has not set a value, the default value is used.
from collections import ChainMap defaults = {"theme": "light", "lang": "en"} user = {"theme": "dark"} settings = ChainMap(user, defaults) print(settings["theme"]) # dark print(settings["lang"]) # en print(dict(settings)) # {'theme': 'dark', 'lang': 'en'} settings["lang"] = "fr" # a change goes into the first dict print(user) # {'theme': 'dark', 'lang': 'fr'} print(defaults) # {'theme': 'light', 'lang': 'en'}
Common mistakes
A missing key in a Counter gives 0. You get no KeyError, so a typo in a key goes unnoticed.
from collections import Counter counts = Counter(["the", "cat", "the"]) print(counts["dog"]) # 0 print(counts["teh"]) # 0
Reading a defaultdict key creates it. Even a simple read adds the key. Check with in or .get() when you only want to look.
from collections import defaultdict d = defaultdict(list) print(d["Ada"]) # [] print(len(d)) # 1 print("Linus" in d) # False print(len(d)) # 1
Indexing in the middle of a deque is slow. A deque is fast at the ends only. If you need items[i] often, use a list.
A namedtuple cannot change. p.price = 5.0 raises AttributeError. Make a changed copy with _replace.
from collections import namedtuple Product = namedtuple("Product", ["name", "price"]) p = Product("tea", 4.5) try: p.price = 5.0 except AttributeError as e: print("AttributeError:", e) # AttributeError: can't set attribute p = p._replace(price=5.0) print(p.price) # 5.0
subtract() keeps negative counts. The - operator drops counts of 0 or less, but the subtract() method keeps them.
from collections import Counter stock = Counter(tea=1) stock.subtract(tea=3) print(stock) # Counter({'tea': -2}) print(Counter(tea=1) - Counter(tea=3)) # Counter()
Found this useful? Share it with a friend who’s learning Python.