defaultdict and OrderedDict in Python
- defaultdict(list) eliminates the if-key-not-in-dict pattern for grouping operations.
- The factory function (list, int, set, or a lambda) is called with no arguments when a missing key is accessed.
- Accessing a missing key in defaultdict creates it — be aware of this when iterating.
defaultdict automatically creates a default value when you access a missing key — no more checking 'if key in dict' before appending. Pass the factory function: defaultdict(list) creates an empty list for missing keys. OrderedDict preserves insertion order (as does regular dict since Python 3.7) and adds move_to_end() and a meaningful __eq__ that considers order.
defaultdict — No More KeyError
from collections import defaultdict # Group words by their first letter words = ['apple', 'avocado', 'banana', 'blueberry', 'cherry', 'apricot'] # Without defaultdict — verbose groups = {} for word in words: if word[0] not in groups: # check before every append groups[word[0]] = [] groups[word[0]].append(word) # With defaultdict — clean groups = defaultdict(list) # list() called for every new key for word in words: groups[word[0]].append(word) # KeyError impossible print(dict(groups)) # {'a': ['apple', 'avocado', 'apricot'], 'b': ['banana', 'blueberry'], 'c': ['cherry']} # Count occurrences counts = defaultdict(int) # int() returns 0 for word in words: counts[word[0]] += 1 print(dict(counts)) # {'a': 3, 'b': 2, 'c': 1}
{'a': 3, 'b': 2, 'c': 1}
defaultdict with Custom Factories
from collections import defaultdict # Any callable works as a factory dd_set = defaultdict(set) # empty set for missing keys dd_zero = defaultdict(lambda: 0) # 0 for missing keys dd_const = defaultdict(lambda: 'N/A') # string constant # Nested defaultdict — for 2-level grouping transactions = [ ('2026-03-01', 'Engineering', 500), ('2026-03-01', 'Marketing', 300), ('2026-03-02', 'Engineering', 700), ] # Date → Department → total monthly = defaultdict(lambda: defaultdict(int)) for date, dept, amount in transactions: monthly[date][dept] += amount print(dict(monthly['2026-03-01'])) # {'Engineering': 500, 'Marketing': 300}
OrderedDict — When It Still Matters
from collections import OrderedDict # Since Python 3.7, regular dicts maintain insertion order # So when is OrderedDict useful? # 1. Order-sensitive equality od1 = OrderedDict([('a', 1), ('b', 2)]) od2 = OrderedDict([('b', 2), ('a', 1)]) regular1 = {'a': 1, 'b': 2} regular2 = {'b': 2, 'a': 1} print(od1 == od2) # False — order matters for OrderedDict print(regular1 == regular2) # True — regular dict ignores order # 2. move_to_end() — useful for LRU patterns cache = OrderedDict() cache['page1'] = 'content1' cache['page2'] = 'content2' cache['page3'] = 'content3' cache.move_to_end('page1') # move to most recently used print(list(cache.keys())) # ['page2', 'page3', 'page1'] cache.move_to_end('page2', last=False) # move to front (LRU = least recently used) print(list(cache.keys())) # ['page2', 'page3', 'page1']
True
['page2', 'page3', 'page1']
['page2', 'page3', 'page1']
🎯 Key Takeaways
- defaultdict(list) eliminates the if-key-not-in-dict pattern for grouping operations.
- The factory function (list, int, set, or a lambda) is called with no arguments when a missing key is accessed.
- Accessing a missing key in defaultdict creates it — be aware of this when iterating.
- Regular dicts maintain insertion order since Python 3.7, so OrderedDict is mostly only needed for order-aware equality and
move_to_end(). - For counting, consider Counter from collections — it provides
most_common()and is more expressive than defaultdict(int).
Interview Questions on This Topic
- QWhat problem does defaultdict solve compared to a regular dict?
- QIs OrderedDict still useful in Python 3.7 and later?
- QHow would you group a list of items by a property without using defaultdict?
Frequently Asked Questions
Does accessing a missing key in defaultdict modify the dictionary?
Yes — that is the point. When you access dd['missing_key'], it calls the factory function, stores the result under 'missing_key', and returns it. This means if you iterate and access keys conditionally, you may end up with more keys than you intended.
Should I use OrderedDict or a regular dict in Python 3.7+?
Use a regular dict for most cases — insertion order is guaranteed. Use OrderedDict when you need order-sensitive equality (two dicts with the same items in different order should compare as unequal) or when you need move_to_end().
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.