Skip to the content.

Author: Amin Boulouma, Software Engineer Github source code: https://github.com/aminblm/ai_systems_design_from_scratch Engineering Blog: https://aminblm.github.io/ai_systems_design_from_scratch/blog/

3 Pro-Level Ways to Use collections.defaultdict for cleaner code

In Python, accessing a missing key in a standard dict raises a KeyError. We have all written the defensive boilerplate: if key not in d: d[key] = []. This is the definition of “noisy” code. The collections.defaultdict class is the enterprise-grade solution for eliminating this clutter, providing a factory function to automatically initialize missing keys.


The Core Concept

A defaultdict is a subclass of the built-in dict that overrides one method (__missing__) to call a default factory function. When you access a key that does not exist, it doesn’t fail; it creates the entry using your specified factory type (e.g., list, int, or a custom function).

Glossary for Beginners


Why We Choose defaultdict over Standard dict

We choose defaultdict because it treats data collection as a primary operation rather than a conditional check. By defining the “default” state of our data structure upfront, we remove the need for if/else branching throughout our business logic.

Why X over Y? We choose defaultdict over dict.setdefault() when the default value is complex or needs to be calculated repeatedly. setdefault creates a new object every time it is called, even if it isn’t used; defaultdict only invokes the factory when the key is truly missing, which is more performant in tight loops.


Implementation: The defaultdict Pattern

Simple Example: Grouping Data

from collections import defaultdict

# Grouping items by category without manual key checks
data = [("fruit", "apple"), ("veg", "carrot"), ("fruit", "banana")]
grouped = defaultdict(list)

for category, item in data:
    grouped[category].append(item)

print(dict(grouped)) 
# Output: {'fruit': ['apple', 'banana'], 'veg': ['carrot']}

Complex Example: Production-Grade Counter

In distributed systems, we often need to aggregate metrics across multiple sources. Using defaultdict(int) acts as a high-performance counter.

from collections import defaultdict
from typing import List

def aggregate_metrics(events: List[str]) -> dict:
    # Factory 'int' returns 0 for missing keys
    metrics = defaultdict(int)
    
    for event in events:
        metrics[event] += 1
        
    return dict(metrics)

# Usage
event_stream = ["login", "click", "login", "purchase", "login"]
print(aggregate_metrics(event_stream))
# Output: {'login': 3, 'click': 1, 'purchase': 1}

Quick Reference: Default Factories

Factory Use Case Result on Missing Key
list Grouping items Returns []
int Counting occurrences Returns 0
set Deduplicating values Returns set()
lambda: 0.0 Custom defaults Returns 0.0

Developer Checklist

TL;DR Summary

Stop writing if key not in d boilerplate. Use defaultdict to define your data structures at initialization. It makes your code more declarative, faster to read, and less prone to logic errors during dictionary initialization. Always cast to dict() if you need to pass the result to downstream systems that expect standard dictionary types.