W3docs

Python Dictionaries

Learn Python dictionaries: create, access, update, delete, iterate, and use comprehensions with clear examples and common gotchas.

A Python dictionary is a mutable, ordered collection of key-value pairs. Unlike lists or tuples (which use integer positions), a dictionary lets you label each value with a meaningful key — making lookups fast and code more readable. Dictionaries are one of Python's most-used built-in types and are the backbone of JSON handling, configuration management, word counting, and much more.

This chapter covers everything you need to know to use dictionaries confidently: creation, access, modification, deletion, iteration, comprehensions, and common gotchas.

Related chapters: Dictionary Methods | Nested Dictionaries | Loop Dictionaries | Copy Dictionaries | Python Comprehensions

What is a Dictionary?

A dictionary maps unique, hashable keys to values of any type. The key rule: keys must be immutable — strings, numbers, and tuples of immutables all qualify; lists and other dictionaries do not.

# string keys → integer values
inventory = {'apple': 10, 'banana': 5, 'orange': 8}

# mixed value types are fine
person = {'name': 'Alice', 'age': 30, 'active': True}

# tuple keys work because tuples are immutable
grid = {(0, 0): 'origin', (1, 0): 'east', (0, 1): 'north'}

Since Python 3.7, dictionaries maintain insertion order — iterating a dictionary always yields keys in the order they were added.

Creating a Dictionary

Curly-brace literal

The most common way: separate key-value pairs with commas, use a colon between each key and its value, and wrap the whole thing in {}.

config = {'host': 'localhost', 'port': 5432, 'debug': True}
print(config)
# {'host': 'localhost', 'port': 5432, 'debug': True}

An empty dictionary is just {}.

empty = {}
print(type(empty))  # <class 'dict'>

dict() constructor

Pass keyword arguments to dict() when your keys are valid Python identifiers:

config = dict(host='localhost', port=5432, debug=True)
print(config)
# {'host': 'localhost', 'port': 5432, 'debug': True}

You can also pass an iterable of two-element sequences:

pairs = [('x', 10), ('y', 20)]
point = dict(pairs)
print(point)  # {'x': 10, 'y': 20}

dict.fromkeys()

Create a dictionary from a list of keys, all sharing the same initial value:

defaults = dict.fromkeys(['timeout', 'retries', 'verbose'], 0)
print(defaults)
# {'timeout': 0, 'retries': 0, 'verbose': 0}

Gotcha: if the default value is a mutable object (like a list), all keys will share the same object. Use a dictionary comprehension instead (shown below).

Dictionary comprehension

A concise way to build a dictionary from any iterable:

squares = {x: x ** 2 for x in range(1, 6)}
print(squares)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

See Python Comprehensions for the full syntax.

Accessing Values

Square-bracket lookup

inventory = {'apple': 10, 'banana': 5, 'orange': 8}
print(inventory['apple'])   # 10

If the key does not exist, Python raises a KeyError:

try:
    print(inventory['grape'])
except KeyError as e:
    print(f'KeyError: {e}')   # KeyError: 'grape'

get() — safe lookup with a default

dict.get(key, default) returns the value if the key exists, or default (which is None if omitted) otherwise — no exception raised.

print(inventory.get('grape'))       # None
print(inventory.get('grape', 0))    # 0
print(inventory.get('apple', 0))    # 10

Use .get() any time you are not sure whether a key exists. Use bracket lookup when the key must be there — a missing key is then a genuine bug that should surface as an error.

Checking whether a key exists

print('apple' in inventory)    # True
print('grape' in inventory)    # False
print('grape' not in inventory) # True

in tests keys, not values. It runs in O(1) average time — constant, no matter how large the dictionary.

Adding and Updating Items

Add a new key-value pair

Assign to a key that does not yet exist:

inventory['grape'] = 12
print(inventory)
# {'apple': 10, 'banana': 5, 'orange': 8, 'grape': 12}

Update an existing value

Assign to a key that already exists — the old value is replaced:

inventory['apple'] = 15
print(inventory['apple'])  # 15

update() — bulk add or overwrite

Pass another dictionary or an iterable of key-value pairs:

inventory.update({'banana': 7, 'mango': 3})
print(inventory)
# {'apple': 15, 'banana': 7, 'orange': 8, 'grape': 12, 'mango': 3}

Merge with | (Python 3.9+)

The | operator creates a new merged dictionary. If a key appears in both, the right-hand side wins:

a = {'x': 1, 'y': 2}
b = {'y': 99, 'z': 3}
merged = a | b
print(merged)   # {'x': 1, 'y': 99, 'z': 3}

Use |= to merge b into a in-place — same as a.update(b) but more readable.

setdefault() — insert only when missing

dict.setdefault(key, default) inserts the key with the default value if it is absent, then always returns the value for that key. This is more efficient than the pattern if key not in d: d[key] = default.

scores = {'Alice': 95}
scores.setdefault('Bob', 0)     # Bob absent → inserts 0, returns 0
scores.setdefault('Alice', 0)   # Alice present → does nothing, returns 95
print(scores)   # {'Alice': 95, 'Bob': 0}

A classic use case is grouping items:

words = ['cat', 'car', 'bus', 'can', 'bat']
groups = {}
for word in words:
    groups.setdefault(word[0], []).append(word)
print(groups)
# {'c': ['cat', 'car', 'can'], 'b': ['bus', 'bat']}

Removing Items

del — remove by key

inventory = {'apple': 15, 'banana': 7, 'orange': 8, 'grape': 12}
del inventory['orange']
print(inventory)
# {'apple': 15, 'banana': 7, 'grape': 12}

del raises KeyError if the key is missing. To avoid this, check with in first, or use pop() with a default.

pop() — remove and return the value

removed = inventory.pop('grape')
print(removed)      # 12
print(inventory)    # {'apple': 15, 'banana': 7}

Supply a second argument to avoid KeyError when the key might be absent:

val = inventory.pop('pear', 'not found')
print(val)   # not found

popitem() — remove the last inserted item (Python 3.7+)

d = {'a': 1, 'b': 2, 'c': 3}
last = d.popitem()
print(last)   # ('c', 3)
print(d)      # {'a': 1, 'b': 2}

clear() — remove everything

d = {'a': 1, 'b': 2}
d.clear()
print(d)   # {}

Iterating Over a Dictionary

Three views let you iterate over different parts of a dictionary. All three are live views — they reflect changes to the dictionary without needing to be recreated.

Keys (default iteration)

Iterating directly over a dictionary yields its keys:

person = {'name': 'Alice', 'age': 30, 'city': 'Paris'}

for key in person:
    print(key)
# name
# age
# city

person.keys() returns the same keys as an explicit dict_keys view object.

Values

for val in person.values():
    print(val)
# Alice
# 30
# Paris

Key-value pairs

for key, val in person.items():
    print(f'{key}: {val}')
# name: Alice
# age: 30
# city: Paris

.items() is the most common choice when you need both key and value inside the loop. See Loop Dictionaries for advanced patterns.

Views are live

d = {'a': 1}
keys_view = d.keys()
d['b'] = 2
print(keys_view)   # dict_keys(['a', 'b'])  ← reflects the new key

Dictionary Comprehensions

Dictionary comprehensions let you build or transform dictionaries in a single readable expression.

Filter by value

inventory = {'apple': 15, 'banana': 7, 'orange': 8, 'grape': 12, 'mango': 3}
popular = {k: v for k, v in inventory.items() if v >= 8}
print(popular)
# {'apple': 15, 'orange': 8, 'grape': 12}

Invert a dictionary (swap keys and values)

original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}
print(swapped)   # {1: 'a', 2: 'b', 3: 'c'}

This only works safely when all values are unique.

Word frequency counter

sentence = 'the quick brown fox jumps over the lazy dog'
freq = {}
for word in sentence.split():
    freq[word] = freq.get(word, 0) + 1
print(freq)
# {'the': 2, 'quick': 1, 'brown': 1, 'fox': 1, ...}

Useful Built-in Functions

OperationExampleResult
Lengthlen(inventory)number of key-value pairs
Copy (shallow)inventory.copy()new dict, same references
Convert to list of keyslist(inventory)['apple', 'banana', ...]
Sorted keyssorted(inventory)['apple', 'banana', ...] alphabetically
All keys truthy?all(inventory.values())True / False
Any key truthy?any(inventory.values())True / False

For a full reference of every dictionary method (copy, fromkeys, update, setdefault, popitem, and more), see Python Dictionary Methods.

Common Gotchas

1. KeyError on missing key — always use .get() or in when a key might not exist.

2. Unhashable key types — lists cannot be dictionary keys because they are mutable. Use a tuple instead:

# This raises TypeError: unhashable type: 'list'
# bad = {[1, 2]: 'value'}

# Use a tuple:
coords = {(1, 2): 'A', (3, 4): 'B'}
print(coords[(1, 2)])  # A

3. Mutating while iterating — adding or removing keys while looping over a dictionary raises RuntimeError. Take a snapshot first:

d = {'a': 1, 'b': 2, 'c': 3}
for key in list(d):          # list() copies the keys
    if d[key] < 2:
        del d[key]
print(d)   # {'b': 2, 'c': 3}

4. Shallow copy vs. deep copydict.copy() and {**d} both do a shallow copy. Nested mutable values (lists, dicts) are still shared. Use copy.deepcopy() when you need fully independent copies. See Copy Dictionaries.

Dictionaries vs. Other Collection Types

Featuredictlisttupleset
OrderedYes (3.7+)YesYesNo
MutableYesYesNoYes
Indexed byKeyIntegerInteger
DuplicatesKeys: no; Values: yesYesYesNo
Main useKey-value mappingOrdered sequenceImmutable sequenceUniqueness / set ops

When you need to look something up by a meaningful label rather than a position, a dictionary is almost always the right choice. For ordered unique values, consider a set. For a simple ordered sequence, use a list.

Practice

Practice
What is true about Python dictionaries according to the information given in the article?
What is true about Python dictionaries according to the information given in the article?
Was this page helpful?