defaultdict
Python defaultdict — The Complete Guide
Master collections.defaultdict to eliminate KeyError, auto-initialize values, and write cleaner grouping and counting code.
Introduction
defaultdict is a dictionary subclass from Python's built-in collections module that automatically
creates a default value for any key that doesn't exist yet. Instead of checking whether a key is present before using it, you
just access it — and defaultdict supplies a sensible default (an empty list, zero, an empty set, etc.) on the fly. This
removes a whole class of KeyError bugs and makes grouping, counting, and accumulating data far cleaner.
defaultdict is a dictionary that creates a default value automatically whenever you touch a missing key.
Real-World Analogy
The Self-Filling Mailboxes
Imagine an apartment building where the moment a new tenant's name is mentioned, an empty mailbox instantly appears for them —
no paperwork, no "does this mailbox exist?" check. defaultdict works the same way: mention a new key and its empty
container is ready and waiting.
Prerequisites
Before You Start
- Python 3.x installed
- Understanding of dictionaries and key/value access
- The module is built in — just
from collections import defaultdict - Familiarity with lists, sets, and integers as values
- Awareness of what a
KeyErroris and when it occurs
The Problem It Solves
With a normal dict, accessing a missing key raises an error, forcing defensive checks.
# Plain dict — verbose and error-prone
d = {}
# This raises KeyError:
# d["fruits"].append("apple")
# You must check first:
if "fruits" not in d:
d["fruits"] = []
d["fruits"].append("apple")
print(d) # {'fruits': ['apple']}
from collections import defaultdict
d = defaultdict(list)
d["fruits"].append("apple") # no check needed!
print(d) # defaultdict(<class 'list'>, {'fruits': ['apple']})
How It Works: default_factory
You pass a factory function to defaultdict. When a missing key is accessed, it calls that function with
no arguments to produce the default value, stores it, and returns it.
Common Default Factories
| Factory | Default Value | Typical Use |
|---|---|---|
int |
0 |
Counting occurrences |
list |
[] |
Grouping items into lists |
set |
set() |
Collecting unique items |
dict |
{} |
Building nested dictionaries |
float |
0.0 |
Summing/averaging |
lambda: X |
Custom value X | Any custom default |
Counting with defaultdict(int)
Each new key starts at 0, so you can increment immediately.
from collections import defaultdict
text = "banana"
counts = defaultdict(int)
for char in text:
counts[char] += 1 # missing keys start at 0
print(dict(counts)) # {'b': 1, 'a': 3, 'n': 2}
Grouping with defaultdict(list)
The most popular use — group items under a key without pre-creating lists.
from collections import defaultdict
people = [
("Engineering", "Alice"),
("Sales", "Bob"),
("Engineering", "Charlie"),
("Sales", "Dave"),
]
teams = defaultdict(list)
for dept, name in people:
teams[dept].append(name)
print(dict(teams))
# {'Engineering': ['Alice', 'Charlie'], 'Sales': ['Bob', 'Dave']}
Unique Collection with defaultdict(set)
from collections import defaultdict
edges = [("A", "B"), ("A", "B"), ("A", "C"), ("B", "C")]
graph = defaultdict(set)
for src, dst in edges:
graph[src].add(dst) # duplicates ignored automatically
print(dict(graph))
# {'A': {'B', 'C'}, 'B': {'C'}}
Nested defaultdict
Use a lambda to build multi-level structures without manual initialization.
from collections import defaultdict
# A dict of dicts of ints
matrix = defaultdict(lambda: defaultdict(int))
matrix["row1"]["col1"] += 5
matrix["row1"]["col2"] += 3
matrix["row2"]["col1"] += 7
print(matrix["row1"]["col1"]) # 5
print(matrix["row2"]["col2"]) # 0 (auto-created)
matrix["row3"]["col9"] silently adds those entries.
defaultdict vs dict.setdefault()
Both avoid KeyError, but they differ in style and repetition.
from collections import defaultdict
# Using dict.setdefault (works, but repeats the default each time)
d1 = {}
d1.setdefault("a", []).append(1)
d1.setdefault("a", []).append(2)
# Using defaultdict (cleaner for repeated use)
d2 = defaultdict(list)
d2["a"].append(1)
d2["a"].append(2)
print(d1) # {'a': [1, 2]}
print(dict(d2)) # {'a': [1, 2]}
| Aspect | defaultdict | dict + setdefault |
|---|---|---|
| Missing key | Auto-creates default | Must pass default each call |
| Readability | Cleaner for repeated access | Verbose if reused |
| Side effect | Access creates the key | Only setdefault creates it |
| Best for | Grouping/counting loops | One-off defaults |
Reading Without Creating Keys
To check a value without accidentally creating it, use .get() or in.
from collections import defaultdict
d = defaultdict(int)
print(d.get("x", 0)) # 0 — does NOT create key "x"
print("x" in d) # False — still not created
print(d["x"]) # 0 — THIS creates key "x"
print("x" in d) # True
Performance
Like a regular dict, defaultdict offers average \(O(1)\) access, insertion, and lookup. Building a grouped structure from
\(n\) items is a single pass:
\[ T_{\text{group}} = O(n) \]
The auto-default adds no meaningful overhead over manual checks — you get cleaner code for free.
Best Practices
Do This
- Use
defaultdict(list)for grouping anddefaultdict(int)for counting - Use
.get()orinto check keys without creating them - Use a
lambdafor nested or custom default values - Convert to a plain
dict()before returning from a function or serializing - Set
d.default_factory = Noneto restore KeyError behaviour if needed - Prefer
Counterwhen pure counting with ranking is the goal
Common Mistakes
defaultdict([]) — this raises a TypeError.
defaultdict(list) — note no parentheses after list.
d[key] silently creates and stores an empty default.
key in d or d.get(key) when you only want to read.
Interview Questions
| Question | Short Answer |
|---|---|
| What is a defaultdict? | A dict subclass that auto-creates a default value for missing keys via a factory. |
| What is default_factory? | The callable used to produce defaults (e.g. int, list). |
| defaultdict vs setdefault? | defaultdict auto-defaults on access; setdefault needs the default every call. |
| Does reading a missing key create it? | Yes — use get() or in to avoid that. |
| How to make nested defaultdicts? | Use a lambda: defaultdict(lambda: defaultdict(int)). |
Quick Revision
| Goal | Code |
|---|---|
| Count items | defaultdict(int) |
| Group into lists | defaultdict(list) |
| Collect unique | defaultdict(set) |
| Nested structure | defaultdict(lambda: defaultdict(int)) |
| Read safely | d.get(key, default) |
Key Takeaways
defaultdict eliminates KeyError by auto-creating defaults from a factory function.
Use int to count, list to group, set for uniqueness, and a lambda
for nesting — just remember that accessing a missing key creates it.