Technology  /  Python

๐Ÿ Python 78 guides ยท updated 2026

From first variable to OOP, generators, and real projects โ€” the language that runs everything from data pipelines to AI agents, taught the practical way.

Pythonโ€™s collections Module: The Data Structures the Standard Library Hides

Pythonโ€™s built-in list, dict, set, and tuple cover most situations. The collections module extends these with specialised types that solve specific patterns more cleanly. If you find yourself writing boilerplate code to handle missing keys, count occurrences, or maintain insertion order, there is likely a collections type that makes it unnecessary.

Counter โ€” Frequency Counting in One Call

Counter takes any iterable and counts how often each element appears.

from collections import Counter
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
freq = Counter(words)
print(freq)
# Counter({'apple': 3, 'banana': 2, 'cherry': 1})
# Most common elements
print(freq.most_common(2))
# [('apple', 3), ('banana', 2)]
# Counter arithmetic
a = Counter(["x", "x", "y"])
b = Counter(["x", "z"])
print(a + b) # Counter({'x': 3, 'y': 1, 'z': 1})
print(a - b) # Counter({'x': 1, 'y': 1}) โ€” subtracts, drops zeros/negatives

Without Counter, you would write a loop, initialise counts to zero, and handle the KeyError on the first occurrence. Counter handles all of that.

defaultdict โ€” No More Missing Key Checks

A defaultdict calls a factory function to supply a default value whenever a key does not exist. No more if key not in d: d[key] = [].

from collections import defaultdict
# Group words by their first letter
words = ["ant", "bear", "cat", "alligator", "buffalo", "crane"]
by_letter = defaultdict(list)
for word in words:
by_letter[word[0]].append(word) # no KeyError โ€” missing key creates []
print(dict(by_letter))
# {'a': ['ant', 'alligator'], 'b': ['bear', 'buffalo'], 'c': ['cat', 'crane']}
# Counting with defaultdict(int)
sentence = "the cat sat on the mat"
word_count = defaultdict(int)
for word in sentence.split():
word_count[word] += 1 # missing key starts at 0
print(dict(word_count))
# {'the': 2, 'cat': 1, 'sat': 1, 'on': 1, 'mat': 1}

deque โ€” O(1) Appends and Pops from Both Ends

A deque (double-ended queue) supports O(1) appends and pops from either end. list.pop(0) is O(n) because it shifts every element. deque.popleft() is O(1).

from collections import deque
# Queue (FIFO) โ€” first in, first out
queue = deque()
queue.append("task1")
queue.append("task2")
queue.append("task3")
print(queue.popleft()) # "task1" โ€” O(1)
# Bounded recent history
recent = deque(maxlen=3)
for item in range(6):
recent.append(item)
print(list(recent))
# Keeps only the 3 most recent items

namedtuple โ€” Readable Tuples with Named Fields

namedtuple creates a tuple subclass whose elements have names. You get the immutability and memory efficiency of a tuple, plus the readability of attribute access.

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y) # 3 4
print(p[0], p[1]) # 3 4 โ€” still works as a regular tuple
print(p._asdict()) # OrderedDict([('x', 3), ('y', 4)])

OrderedDict โ€” When Dict Order Must Be Explicit

Since Python 3.7, regular dicts maintain insertion order. OrderedDict is useful when the order itself is meaningful, or when you need move_to_end():

from collections import OrderedDict
od = OrderedDict()
od['first'] = 1
od['second'] = 2
od['third'] = 3
od.move_to_end('first') # move 'first' to the end
print(list(od.keys())) # ['second', 'third', 'first']
# LRU cache-like pattern: evict the least-recently-used item
od.move_to_end('second', last=False) # move to front
print(list(od.keys())) # ['second', 'third', 'first']

ChainMap โ€” Multiple Dicts as One View

ChainMap combines multiple dicts into a single view. Lookups search each dict in order without merging them. Writes go to the first dict only.

from collections import ChainMap
defaults = {"theme": "light", "font_size": 12, "debug": False}
user_prefs = {"theme": "dark"}
cli_args = {"debug": True}
config = ChainMap(cli_args, user_prefs, defaults)
print(config["theme"]) # "dark" โ€” from user_prefs
print(config["font_size"]) # 12 โ€” from defaults
print(config["debug"]) # True โ€” from cli_args

This is the cleanest way to implement layered configuration (CLI overrides env overrides config file overrides defaults).

Quick Comparison Table

TypeWhen to use
CounterCounting occurrences, frequency tables, word counts
defaultdictGrouping, accumulating into lists/sets per key
dequeQueues, sliding windows, bounded history
namedtupleLightweight immutable records with meaningful field names
OrderedDictWhen move_to_end() is needed, or order is semantically significant
ChainMapLayered configuration, variable scoping simulation