python
dictionary
key
update
duplicate

Python update a key in dict if it doesn't exist

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Initializing a dictionary key only when it is missing is a frequent Python pattern in counting, grouping, and caching code. Python offers several ways to do this, and choosing the right one affects readability and correctness in subtle ways. The best method depends on whether the default value is simple, mutable, or expensive to create.

Direct Conditional Check

The most explicit approach is checking membership first.

python
1data = {}
2key = "retry_count"
3
4if key not in data:
5    data[key] = 0
6
7print(data)

This is easy to read and clear for newcomers.

Using setdefault

setdefault returns existing value or inserts the default when missing.

python
1data = {"retry_count": 2}
2value = data.setdefault("retry_count", 0)
3print(value)  # 2
4
5value2 = data.setdefault("timeout", 30)
6print(value2)  # 30
7print(data)

This is concise, especially when you need the resulting value immediately.

Mutable Defaults and Shared-Object Trap

Be careful with mutable default objects. setdefault inserts exactly the object you pass.

python
1groups = {}
2
3lst = groups.setdefault("team_a", [])
4lst.append("alice")
5
6print(groups)

This is fine when intentional, but avoid reusing the same mutable object across keys by accident.

Safer per-key initialization pattern:

python
1groups = {}
2for user, team in [("alice", "a"), ("bob", "a"), ("carol", "b")]:
3    if team not in groups:
4        groups[team] = []
5    groups[team].append(user)

defaultdict for Frequent Missing Keys

For repeated missing-key operations, collections.defaultdict is often cleaner.

python
1from collections import defaultdict
2
3counts = defaultdict(int)
4for word in ["a", "b", "a"]:
5    counts[word] += 1
6
7print(dict(counts))

defaultdict(list) is also great for grouping.

python
1from collections import defaultdict
2
3groups = defaultdict(list)
4groups["x"].append(1)
5groups["x"].append(2)
6print(dict(groups))

Expensive Defaults and Lazy Evaluation

A subtle issue: function arguments are evaluated before setdefault runs. If default creation is expensive, that cost happens even when key already exists.

python
1def expensive_default():
2    print("building default")
3    return [0] * 1_000_000
4
5cache = {"k": [1]}
6cache.setdefault("k", expensive_default())  # expensive function still executes

For lazy default creation, use explicit conditional logic.

python
cache = {"k": [1]}
if "k" not in cache:
    cache["k"] = expensive_default()

Nested Dictionary Initialization

For nested structures, combine checks carefully.

python
1store = {}
2
3user = "u1"
4metric = "clicks"
5
6if user not in store:
7    store[user] = {}
8if metric not in store[user]:
9    store[user][metric] = 0
10
11store[user][metric] += 1
12print(store)

For heavy nested updates, helper functions or defaultdict nesting can reduce boilerplate.

Concurrency Note

In multithreaded code, check-then-set is not atomic across threads without synchronization. If concurrent writes are possible, use locks or thread-safe coordination patterns.

For single-threaded scripts and most request-local code, regular dictionary methods are sufficient.

Practical Logging and Metrics Pattern

Dictionary initialization often appears in metrics aggregation. A clean pattern is initializing counters once and then incrementing in one place, so metric keys remain stable across request handlers. This helps avoid typos and inconsistent key naming that produce fragmented observability dashboards.

Choosing the Right Pattern

Quick guidance:

  • use if key not in d when readability matters most
  • use setdefault for concise local initialization
  • use defaultdict for repeated bulk operations
  • avoid eager expensive defaults with setdefault

Consistency within a codebase is usually more valuable than micro-optimization.

Common Pitfalls

  • Using setdefault with expensive default creation and paying unnecessary cost.
  • Reusing mutable default objects unintentionally across keys.
  • Mixing initialization styles inconsistently across one module.
  • Assuming check-then-set is thread-safe without synchronization.
  • Using defaultdict where plain dict would be clearer for small logic blocks.

Summary

  • Python offers multiple safe ways to initialize missing dictionary keys.
  • 'setdefault is concise but has eager default-evaluation behavior.'
  • 'defaultdict is excellent for frequent counting and grouping tasks.'
  • Explicit condition checks are often clearest for complex initialization.
  • Choose one style per context and document team conventions.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.