dict.setdefault()

dict.get that also writes: missing keys get the default stored, then returned.

Dict methodPython 2.0+Live demo
Common call
groups.setdefault(key, []).append(item)
Returns
existing value, or the default (now stored)
Replaces
get reads only; setdefault reads AND writes on miss
Watch out
the default is evaluated even when the key exists
dict.setdefault(keykey — The key to look up (and possibly create).type: hashable · required, defaultdefault — Stored under key and returned when the key is absent.type: Any · default: None=None)
→ Any

Demo

Live evaluation
Try:
Inputs
dictdictkey: value pairs
keystrkey to look up
defaultAnyempty = None
Output
{'a': '1', 'b': '2'}.setdefault('a', '0')
'1'

The demo shows the RETURN value. The side effect is the point though: on a miss, real Python also stores the default — after {"a": 1}.setdefault("z", 0) the dict is {"a": 1, "z": 0}.

Parameters

NameTypeRequiredDescription
keyhashableyesThe key to look up (and possibly create).
defaultAnyno (None)Stored under key and returned when the key is absent.

Return value

Any — The existing value when the key is present; otherwise the default — which is also STORED under the key.

Common patterns

Grouping into lists
The canonical setdefault idiom — initialize-and-append in one line.
groups = {}
for item in items:
    groups.setdefault(item.kind, []).append(item)
Nested dict creation
Build one level at a time without membership checks.
tree.setdefault(a, {}).setdefault(b, {})[c] = value
First-write-wins cache
Later calls with the same key keep the original value.
canonical = seen.setdefault(name.lower(), name)

Examples

1. Key present — value returned, dict untouched
{"a": 1}.setdefault("a", 0)
Returns
1
2. Key missing — default stored and returned
{"a": 1}.setdefault("z", 0)
Returns
0
3. Default defaults to None
{}.setdefault("k")
Returns
None

Pitfalls

1. The default is evaluated eagerly
Even when the key exists, the default expression runs — costly defaults hurt.
Wasted work
d.setdefault(k, expensive())
expensive() runs on every call
Fix
if k not in d:
    d[k] = expensive()
expensive() only on miss
2. Mutable default is shared per call, not per key
Each setdefault call needs a FRESH list/dict literal — do not hoist it.
Shared list
empty = []
d.setdefault("a", empty).append(1)
d.setdefault("b", empty).append(2)
d['a'] is d['b'] — both [1, 2]
Fix
d.setdefault("a", []).append(1)
d.setdefault("b", []).append(2)
{'a': [1], 'b': [2]}
3. Heavy grouping wants defaultdict
setdefault re-evaluates the default per call; defaultdict builds it only on miss.
Fine but noisy
groups.setdefault(k, []).append(x)
new [] built every call
At scale
from collections import defaultdict
groups = defaultdict(list)
groups[k].append(x)
[] built only on first miss

When to use

Use it
  • Occasional initialize-if-missing on a plain dict
  • Grouping / nesting without pre-checks
  • First-write-wins registries
Reach for something else
  • Read-only default → dict.get
  • Every key needs the same factory → collections.defaultdict
  • Expensive defaults → explicit "if key not in d" guard

Notes

Complexity
O(1) average — single hash lookup, insert on miss
Return
stored value; dict mutates only on miss
CPython impl
Objects/dictobject.c :: dict_setdefault_impl
Memory
Inserts one entry on miss
Thread-safe
The lookup-insert is atomic in CPython — handy for simple caches

FAQ

When most lookups create the key and the default comes from a factory (list, int, set). setdefault wins for one-off use on dicts you do not control the type of.

History

2.0
Core dict method, unchanged semantics since.