Try the tool first:
Open the Interactive Python Dictionary Methods Cheat Sheet40+ methods, real-time search, category filtering, one-click copy. 100% client-side, no signup.
Python dictionaries are the most versatile data structure in the language. Every Python developer uses them dozens of times per day. Yet even experienced developers occasionally pause to remember: Does get() raise an error? What's the difference between update() and |=? How do I merge dicts without mutating the originals? What's the fastest way to count frequencies?
This comprehensive guide covers every Python dictionary method, operator, and common pattern you need to know. Each section includes practical code examples you can copy and run immediately. By the end, you'll know dictionaries inside and out — and you'll have a free interactive cheat sheet bookmarked for quick reference.
What Are Python Dictionaries?
A Python dictionary is an unordered, mutable mapping of hashable keys to arbitrary values. Dictionaries are implemented as hash tables in CPython, which gives them O(1) average-case time complexity for lookups, insertions, and deletions. They are defined with curly braces or the dict() constructor.
# Creating dictionaries
empty = {}
empty2 = dict()
# Literal syntax
person = {"name": "Alice", "age": 30, "city": "NYC"}
# From keyword arguments
person2 = dict(name="Bob", age=25, city="LA")
# From a list of tuples
items = [("a", 1), ("b", 2), ("c", 3)]
d = dict(items)
# From two parallel iterables with zip()
keys = ["x", "y", "z"]
values = [10, 20, 30]
d = dict(zip(keys, values))
# Fromkeys — initialize with default value
keys = ["read", "write", "execute"]
perms = dict.fromkeys(keys, False)
# perms → {'read': False, 'write': False, 'execute': False} Key Requirements: Hashability
Dictionary keys must be hashable. Immutable built-in types — strings, numbers, tuples (if elements are hashable), frozensets — are hashable. Mutable types like lists, dictionaries, and sets cannot be used as keys.
# Valid keys
{"name": "Alice"} # string key
{42: "answer"} # int key
{(1, 2): "point"} # tuple key (elements are hashable)
# Invalid keys — TypeError
{[1, 2]: "list"} # list is unhashable
{{"a": 1}: "dict"} # dict is unhashable Performance note: Dictionaries in CPython 3.6+ maintain insertion order as an implementation detail, and this became a language guarantee in Python 3.7+. This means iterating over a dict yields keys in the order they were inserted.
Accessing Values
Python provides multiple ways to retrieve values from a dictionary. Choosing the right method prevents KeyError exceptions and makes your code more robust.
Bracket Notation — d[key]
The most direct way to access a value. It raises KeyError if the key does not exist. Use this when you are certain the key is present.
d = {"name": "Alice", "age": 30}
name = d["name"] # "Alice"
age = d["age"] # 30
# Missing key raises KeyError
city = d["city"] # KeyError: 'city' get() — Safe Access with Default
d.get(key, default) returns the value for key if it exists, otherwise default (or None if default is omitted). It never raises KeyError.
d = {"name": "Alice", "age": 30}
city = d.get("city") # None
city = d.get("city", "NYC") # "NYC"
name = d.get("name", "Unknown") # "Alice" (default ignored)
# Common pattern: counting with get()
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
counts = {}
for w in words:
counts[w] = counts.get(w, 0) + 1
# counts → {'apple': 3, 'banana': 2, 'cherry': 1} setdefault() — Get or Create
d.setdefault(key, default) returns the value for key if it exists. If not, it inserts key with default and returns default. This is useful for lazy initialization.
d = {"name": "Alice"}
# Key exists — returns existing value, no change
age = d.setdefault("age", 25) # age = 30 if "age" was 30
# Key missing — inserts and returns default
city = d.setdefault("city", "NYC")
# d → {"name": "Alice", "city": "NYC"}
# Grouping pattern
emails = [
("sales", "a@example.com"),
("support", "b@example.com"),
("sales", "c@example.com"),
]
groups = {}
for dept, email in emails:
groups.setdefault(dept, []).append(email)
# groups → {'sales': ['a@...', 'c@...'], 'support': ['b@...']} collections.defaultdict — Automatic Defaults
For automatic default value generation (especially mutable defaults), collections.defaultdict is cleaner than setdefault(). Pass a factory function like list, set, int, or dict.
from collections import defaultdict
# Automatic list
dd = defaultdict(list)
dd["fruits"].append("apple")
dd["fruits"].append("banana")
# dd → defaultdict({'fruits': ['apple', 'banana']})
# Automatic int (for counting)
counts = defaultdict(int)
for w in ["apple", "banana", "apple"]:
counts[w] += 1
# counts → defaultdict({'apple': 2, 'banana': 1})
# Automatic set (for deduplication)
unique = defaultdict(set)
unique["tags"].add("python")
unique["tags"].add("python") # ignored — set deduplicates
# unique → defaultdict({'tags': {'python'}}) Rule of thumb: Use d[key] when the key must exist (fail fast). Use d.get() for optional values with a simple default. Use setdefault() for lazy initialization. Use defaultdict when you need automatic factory-generated defaults, especially for mutable types.
Adding and Updating Elements
Direct Assignment
Assigning to a key inserts it if missing, or updates the value if present. This is the simplest and fastest way to add or update a single key.
d = {"name": "Alice"}
d["age"] = 30 # Insert new key
d["name"] = "Bob" # Update existing key
# d → {'name': 'Bob', 'age': 30} update() — Merge Another Mapping
d.update(other) merges key-value pairs from other into d. Existing keys are overwritten. other can be a dictionary, an iterable of key-value pairs, or keyword arguments.
d = {"a": 1, "b": 2}
# From another dict
d.update({"b": 20, "c": 3})
# d → {'a': 1, 'b': 20, 'c': 3}
# From iterable of tuples
d.update([("d", 4), ("e", 5)])
# From keyword arguments
d.update(f=6, g=7)
# d → {'a': 1, 'b': 20, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7} |= Operator — In-Place Merge (Python 3.9+)
The |= operator updates the left dictionary in-place with key-value pairs from the right. It is equivalent to update() but can be used in expressions.
d = {"a": 1, "b": 2}
d |= {"b": 20, "c": 3}
# d → {'a': 1, 'b': 20, 'c': 3} Removing Elements
del — Delete by Key
The del statement removes a key-value pair. It raises KeyError if the key does not exist. del is a statement, not a method — it does not return a value.
d = {"a": 1, "b": 2, "c": 3}
del d["b"]
# d → {'a': 1, 'c': 3}
# Safe deletion with check
if "z" in d:
del d["z"] pop() — Remove and Return
d.pop(key, default) removes key and returns its value. If the key is missing and no default is provided, it raises KeyError. With a default, it returns the default without modifying the dictionary.
d = {"a": 1, "b": 2, "c": 3}
val = d.pop("b") # val = 2; d → {'a': 1, 'c': 3}
val = d.pop("z", None) # val = None; d unchanged
# Common pattern: extract and process
config = {"debug": True, "timeout": 30}
debug = config.pop("debug", False)
# Process with debug flag, config no longer contains it popitem() — Remove and Return Arbitrary Item
d.popitem() removes and returns the last inserted key-value pair as a tuple (LIFO order). In Python 3.7+, this is guaranteed to be the most recently added item. Raises KeyError if the dictionary is empty.
d = {"a": 1, "b": 2, "c": 3}
item = d.popitem()
# item → ('c', 3); d → {'a': 1, 'b': 2}
# LIFO stack behavior
stack = {}
stack["first"] = 1
stack["second"] = 2
k, v = stack.popitem() # ('second', 2) clear() — Remove All Items
d.clear() removes all key-value pairs, leaving an empty dictionary. It is more efficient than repeatedly calling popitem() or reassigning d = (which creates a new object).
d = {"a": 1, "b": 2}
d.clear()
# d → {}
# clear() vs reassigning
d2 = d.copy()
d.clear() # d2 still references original (now empty)
# vs
d = {} # d2 still references the old dict object Querying and Inspecting
keys(), values(), items()
These methods return view objects that reflect the dictionary's current state dynamically. They are not copies — changes to the dictionary are visible in the views.
d = {"a": 1, "b": 2, "c": 3}
k = d.keys() # dict_keys(['a', 'b', 'c'])
v = d.values() # dict_values([1, 2, 3])
items = d.items() # dict_items([('a', 1), ('b', 2), ('c', 3)])
# Views are dynamic
k # dict_keys(['a', 'b', 'c'])
d["d"] = 4
k # dict_keys(['a', 'b', 'c', 'd']) — updated!
# Iteration
for key in d.keys():
print(key)
for value in d.values():
print(value)
for key, value in d.items():
print(f"{key} = {value}") Membership Testing — in
The in operator tests key membership (not values). It uses the hash table for O(1) average-case lookup.
d = {"a": 1, "b": 2}
"a" in d # True (key membership)
1 in d # False (values are not checked)
"z" not in d # True
# Check values with .values()
1 in d.values() # True len() and bool()
d = {"a": 1, "b": 2}
len(d) # 2
bool(d) # True
empty = {}
len(empty) # 0
bool(empty) # False
# Idiomatic emptiness check
if d:
print("Dictionary has items")
if not empty:
print("Dictionary is empty") View object surprise: View objects do not support indexing. You cannot do d.keys()[0]. Convert to a list if you need indexing: list(d.keys())[0]. However, this is O(n) and creates a copy — prefer iteration or next(iter(d)) for the first key.
Merging Dictionaries
Python offers multiple ways to combine dictionaries. The right choice depends on whether you want a new dictionary or in-place modification, and which Python version you are using.
| Operator — New Merged Dict (Python 3.9+)
The | operator creates a new dictionary containing all key-value pairs from both operands. The right-hand dictionary's values win on key collisions.
d1 = {"a": 1, "b": 2}
d2 = {"b": 20, "c": 3}
merged = d1 | d2
# merged → {'a': 1, 'b': 20, 'c': 3}
# d1 and d2 are unchanged {**a, **b} — Unpacking Merge
Dictionary unpacking with ** works in Python 3.5+. It creates a new dictionary by unpacking both operands. Rightmost values win on collisions.
d1 = {"a": 1, "b": 2}
d2 = {"b": 20, "c": 3}
merged = {**d1, **d2}
# merged → {'a': 1, 'b': 20, 'c': 3}
# Merge more than two
merged = {**d1, **d2, **{"d": 4}} update() and |= — In-Place Merge
Both modify the left dictionary. See the "Adding and Updating" section for details.
Comparison Table: Merge Methods
| Method | Returns | Modifies Original | Python Version | When to Use |
|---|---|---|---|---|
| d1 | d2 | New dict | No | 3.9+ | New merged dict, clean syntax |
| {**d1, **d2} | New dict | No | 3.5+ | New merged dict, older Python |
| d1.update(d2) | None | Yes (d1) | All | In-place merge, method call |
| d1 |= d2 | None | Yes (d1) | 3.9+ | In-place merge, operator syntax |
Dictionary Comprehensions
Dictionary comprehensions provide a concise, readable way to create dictionaries from iterables. They are generally faster than equivalent for-loops.
Basic Syntax
# Squares mapping
{x: x**2 for x in range(5)}
# → {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# From two lists
keys = ["a", "b", "c"]
values = [1, 2, 3]
{k: v for k, v in zip(keys, values)}
# → {'a': 1, 'b': 2, 'c': 3} Filtered Comprehensions
# Only even keys
{x: x**2 for x in range(10) if x % 2 == 0}
# → {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# Filter by value from existing dict
d = {"a": 1, "b": 20, "c": 3, "d": 40}
{k: v for k, v in d.items() if v > 10}
# → {'b': 20, 'd': 40} Conditional Expressions
# Categorize numbers
nums = [1, 2, 3, 4, 5]
{x: "even" if x % 2 == 0 else "odd" for x in nums}
# → {1: 'odd', 2: 'even', 3: 'odd', 4: 'even', 5: 'odd'}
# Normalize values (cap at 100)
scores = {"Alice": 95, "Bob": 120, "Carol": 88}
{k: (v if v <= 100 else 100) for k, v in scores.items()}
# → {'Alice': 95, 'Bob': 100, 'Carol': 88} Nested and Advanced Patterns
# Invert a dictionary (values become keys)
d = {"a": 1, "b": 2, "c": 3}
{v: k for k, v in d.items()}
# → {1: 'a', 2: 'b', 3: 'c'}
# Swap keys and values with transformation
d = {"Alice": 30, "Bob": 25}
{v: k.upper() for k, v in d.items()}
# → {30: 'ALICE', 25: 'BOB'}
# Group lengths by first letter
words = ["apple", "apricot", "banana", "cherry"]
{w[0]: len(w) for w in words}
# → {'a': 7, 'b': 6, 'c': 6} (last word wins for 'a') Performance tip: Dictionary comprehensions run at C speed inside the interpreter and are typically 20-40% faster than equivalent for-loops. Use them whenever the logic fits on one readable line.
Common Patterns
Frequency Counting
The most common dictionary pattern. Count occurrences of items in an iterable.
from collections import Counter
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
# Using Counter (recommended)
counts = Counter(words)
# Counter({'apple': 3, 'banana': 2, 'cherry': 1})
# Manual with get()
counts = {}
for w in words:
counts[w] = counts.get(w, 0) + 1
# Manual with setdefault()
counts = {}
for w in words:
counts.setdefault(w, 0)
counts[w] += 1
# Most common elements
counts.most_common(2)
# → [('apple', 3), ('banana', 2)] Grouping
Group items by a common key. This pattern appears in data processing pipelines.
from collections import defaultdict
people = [
{"name": "Alice", "dept": "Engineering"},
{"name": "Bob", "dept": "Sales"},
{"name": "Carol", "dept": "Engineering"},
]
# Group by department
by_dept = defaultdict(list)
for p in people:
by_dept[p["dept"]].append(p["name"])
# → {'Engineering': ['Alice', 'Carol'], 'Sales': ['Bob']}
# Group by first letter
words = ["apple", "apricot", "banana", "cherry"]
by_letter = defaultdict(list)
for w in words:
by_letter[w[0]].append(w)
# → {'a': ['apple', 'apricot'], 'b': ['banana'], 'c': ['cherry']} Inverting a Dictionary
Swap keys and values. Be careful — values must be unique or you will lose data.
d = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in d.items()}
# → {1: 'a', 2: 'b', 3: 'c'}
# Handle duplicate values — group by value
from collections import defaultdict
d = {"a": 1, "b": 2, "c": 1}
inverted = defaultdict(list)
for k, v in d.items():
inverted[v].append(k)
# → {1: ['a', 'c'], 2: ['b']} Finding the Key with Maximum Value
scores = {"Alice": 95, "Bob": 87, "Carol": 92}
# Key with max value
best = max(scores, key=scores.get)
# → 'Alice'
# Key with min value
worst = min(scores, key=scores.get)
# → 'Bob'
# Get the actual max value
max_score = max(scores.values())
# → 95 Sorting by Value
scores = {"Alice": 95, "Bob": 87, "Carol": 92}
# Sort by value ascending
sorted(scores.items(), key=lambda x: x[1])
# → [('Bob', 87), ('Carol', 92), ('Alice', 95)]
# Sort by value descending
sorted(scores.items(), key=lambda x: x[1], reverse=True)
# → [('Alice', 95), ('Carol', 92), ('Bob', 87)]
# Convert back to dict (Python 3.7+ preserves order)
dict(sorted(scores.items(), key=lambda x: x[1], reverse=True))
# → {'Alice': 95, 'Carol': 92, 'Bob': 87} Comparison Tables
d[key] vs d.get() vs d.setdefault()
| Feature | d[key] | d.get(key, default) | d.setdefault(key, default) |
|---|---|---|---|
| Missing key behavior | Raises KeyError | Returns default/None | Inserts default, returns it |
| Modifies dictionary | No | No | Yes (if key missing) |
| Use case | Key must exist | Safe read with fallback | Lazy initialization |
| Performance | O(1) | O(1) | O(1) |
| Example result | d["x"] → KeyError | d.get("x", 0) → 0 | d.setdefault("x", 0) → 0, d now has "x" |
update() vs | vs |= vs {**a, **b}
| Feature | update() | | | |= | {**a, **b} |
|---|---|---|---|---|
| Returns | None | New dict | None | New dict |
| Modifies left operand | Yes | No | Yes | No |
| Python version | All | 3.9+ | 3.9+ | 3.5+ |
| Accepts iterables | Yes | Dict only | Dict only | Dict only |
| Syntax style | Method call | Binary operator | Augmented operator | Literal unpacking |
pop() vs del vs popitem()
| Feature | pop(key, default) | del d[key] | popitem() |
|---|---|---|---|
| Returns value | Yes | No | Yes (key, value tuple) |
| Requires key | Yes | Yes | No (removes last inserted) |
| Missing key behavior | Returns default or KeyError | KeyError | KeyError (if empty) |
| Use case | Remove known key, use value | Delete known key | LIFO stack pop |
Performance Considerations
Understanding the time complexity of dictionary operations helps you write efficient code:
| Operation | Time Complexity | Notes |
|---|---|---|
| d[key] (get) | O(1) average | Hash table lookup |
| d[key] = value (set) | O(1) average | Hash table insertion |
| del d[key] | O(1) average | Hash table deletion |
| key in d | O(1) average | Hash table membership |
| len(d) | O(1) | Stored internally |
| Iteration (keys/values/items) | O(n) | Visits all n items |
| d.copy() | O(n) | Shallow copy of all items |
| d | other (merge) | O(n + m) | n = len(d), m = len(other) |
| d.update(other) | O(m) | m = len(other) |
Space complexity: Dictionaries use more memory than lists due to hash table overhead (typically 2-3x the space of a list of the same logical size). This tradeoff gives you O(1) lookups instead of O(n).
Common Gotchas and How to Avoid Them
1. Mutable Default Arguments with dict()
Never use a dictionary as a default argument. It evaluates once at function definition and is shared across all calls.
# WRONG
def add_item(key, value, d={}):
d[key] = value
return d
add_item("a", 1) # {'a': 1}
add_item("b", 2) # {'a': 1, 'b': 2} — same dict!
# CORRECT
def add_item(key, value, d=None):
if d is None:
d = {}
d[key] = value
return d 2. Modifying a Dictionary While Iterating
Adding or removing keys during iteration can raise RuntimeError or skip elements. Iterate over a copy of the keys instead.
# WRONG — RuntimeError in Python 3
d = {"a": 1, "b": 2, "c": 3}
for k in d:
if d[k] < 2:
del d[k]
# CORRECT — iterate over a copy of keys
for k in list(d.keys()):
if d[k] < 2:
del d[k]
# BETTER — dict comprehension
d = {k: v for k, v in d.items() if v >= 2} 3. Unhashable Keys
Lists and dictionaries cannot be used as keys. If you need composite keys, use tuples (which are hashable if their elements are hashable).
# WRONG
d = {[1, 2]: "point"} # TypeError: unhashable type: 'list'
# CORRECT — use tuple
d = {(1, 2): "point"} # OK
# For nested dict keys, serialize or use tuple of items
d = {tuple(sorted({"x": 1}.items())): "value"} 4. View Object Surprises
View objects reflect live dictionary state but do not support indexing. They also become invalid if the dictionary is resized during iteration (though iterating is safe if you only modify values for existing keys).
d = {"a": 1, "b": 2}
keys = d.keys()
# Cannot index
# keys[0] # TypeError
# Safe: iterate and modify values
for k in d.keys():
d[k] *= 2 # OK — keys unchanged
# Unsafe: add/remove during iteration
for k in d.keys():
d["c"] = 3 # RuntimeError: dictionary changed size 5. Shallow Copy Gotchas
d.copy(), dict(d), and {**d} all create shallow copies. Nested mutable objects are shared.
import copy
d = {"a": [1, 2], "b": 3}
c = d.copy()
c["b"] = 99 # d["b"] still 3 (independent)
c["a"].append(3) # d["a"] also [1, 2, 3] (shared!)
# Deep copy for full independence
c = copy.deepcopy(d)
c["a"].append(4) # d["a"] still [1, 2, 3] Try the Interactive Cheat Sheet
We have built a free interactive Python Dictionary Methods Cheat Sheet that puts all 40+ methods at your fingertips. Features include:
- Real-time search — Type any method name or keyword
- Category filtering — Access, Update, Remove, Query, Merge, Comprehensions, Patterns
- Syntax highlighting — Color-coded Python code
- One-click copy — Copy any code example instantly
- Mutates vs Returns New badges — Know which methods modify the original
- Comparison tables — d[key] vs get() vs setdefault(), merge methods
- Pro tips section — Common pitfalls and advanced techniques
Interactive Tool:
Python Dictionary Methods Cheat Sheet — Interactive ReferenceThe Gardener's Greenhouse aesthetic. 100% client-side. No signup required.
Summary
Python dictionaries are the backbone of Python programming. Mastering their methods and patterns makes you write cleaner, more efficient code:
- Use
d[key]when the key must exist; used.get()for safe access with defaults - Use
setdefault()for lazy initialization; preferdefaultdictfor automatic factory defaults - Merge with
|for new dicts (3.9+),{**a, **b}for older Python,update()or|=for in-place - Use
pop()when you need the removed value; usedelwhen you don't; usepopitem()for LIFO stack behavior - Remember that
keys(),values(), anditems()return dynamic views, not copies - Dictionary comprehensions are faster and more readable than equivalent for-loops
- Use
Counterfor frequency counting,defaultdict(list)for grouping - Never modify a dictionary while iterating over it directly
- Never use mutable default arguments
- Use
copy.deepcopy()when you need true independence from nested mutable objects
Bookmark the interactive cheat sheet and the DevToolkit homepage for quick access to all 70+ developer tools.