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(keykeyThe key to look up (and possibly create).type: hashable · required, defaultdefaultStored 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

AnyThe 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.