Python Dictionary Tutorial: Methods, Examples and Common Patterns
Dictionaries are everywhere in Python, from JSON responses to configuration. Learn how to create, read and update them, then pick up the counting, grouping and merging patterns you will use on real projects.
- A Python dictionary stores key and value pairs, and looks up a value by its key very quickly.
- Use
get()when a key may be missing, since square brackets raiseKeyError. - Keys must be hashable, such as strings, numbers or tuples. Lists cannot be keys.
copy()is shallow, so nested data stays shared unless you usedeepcopy.
If a list is a row of numbered boxes, a Python dictionary is a set of labelled ones. You ask for what you want by name rather than position, which is why dictionaries sit behind JSON data, configuration, caches and much of Python itself.
This tutorial starts with the basics and moves on to the patterns that experienced developers use every day. We ran all the examples on Python 3.13, and the output shown comes from that run.
What is a dictionary?
A dictionary, or dict, maps keys to values. Each key appears only once, and values can be anything, including lists or other dictionaries.
| Type | Access by | Ordered | Duplicates | Best for |
|---|---|---|---|---|
list |
Position | Yes | Allowed | Sequences |
tuple |
Position | Yes | Allowed | Fixed records |
set |
Membership only | No | Not allowed | Unique items |
dict |
Key | Insertion order | Keys are unique | Lookups by name |
Since Python 3.7, dictionaries officially keep insertion order. Many older tutorials still call them unordered, which is no longer true. For a broader comparison of the other types, see Python list vs tuple vs set.
Lookups are fast because dictionaries use a hash table. Checking a key takes roughly the same time whether the dictionary holds ten items or a million, whereas searching a list gets slower as it grows.
Create a dictionary
user = {"name": "Asha", "age": 31, "city": "Pune"}
empty = {}
from_keywords = dict(name="Asha", age=31)
from_pairs = dict([("a", 1), ("b", 2)])
Curly braces are the most common form. One trap: {} creates an empty dictionary, not an empty set. For an empty set, you need set().
Read values safely
There are two ways to read a value, and the difference matters when a key might not exist:
user["name"] # 'Asha'
user["email"] # KeyError: 'email'
user.get("email") # None
user.get("email", "not set") # 'not set'
Use square brackets when the key must exist, so that a missing key fails loudly. Use get() when absence is normal, such as an optional field in an API response. To check for a key, use the in operator, which looks at keys rather than values:
"city" in user # True
"Pune" in user # False
Add, update and remove
Assigning to a key updates it if it exists, or adds it if it does not:
user["age"] = 32
user["email"] = "asha@example.com"
print(user)
# {'name': 'Asha', 'age': 32, 'city': 'Pune', 'email': 'asha@example.com'}
Python offers several ways to remove items:
user.pop("city") # returns 'Pune' and removes it
user.pop("phone", None) # no error if it is missing
del user["email"] # removes it, and raises KeyError if missing
user.clear() # empties the dictionary
Use pop() when you want the value back. Passing a default as its second argument avoids an error when the key is not there. popitem() removes and returns the most recently added pair.
Loop over a dictionary
Looping directly gives you the keys. To get both keys and values, use items():
user = {"name": "Asha", "age": 32}
for key, value in user.items():
print(key, "=", value)
# name = Asha
# age = 32
keys(), values() and items() return live views rather than copies, so they reflect later changes. Do not add or remove keys while looping over a dictionary, because Python raises a RuntimeError. Loop over list(user) if you need to do that.
Tip: The in check on a dictionary is fast, but "Pune" in user.values() has to scan every value. If you do this often with large data, your dictionary may be keyed the wrong way round.
Dictionary methods cheat sheet
| Method | What it does |
|---|---|
get(k, default) |
Returns a value, or the default if the key is missing |
keys() |
Returns a view of the keys |
values() |
Returns a view of the values |
items() |
Returns a view of key and value pairs |
update(other) |
Adds or overwrites from another dictionary |
pop(k, default) |
Removes a key and returns its value |
popitem() |
Removes and returns the last added pair |
setdefault(k, v) |
Returns the value, and first inserts v if the key is missing |
copy() |
Returns a shallow copy |
clear() |
Removes everything |
dict.fromkeys(keys, v) |
Builds a dictionary from a list of keys |
Merge dictionaries
A common need is applying user settings on top of defaults. When keys clash, the right-hand side wins:
defaults = {"theme": "light", "lang": "en"}
saved = {"theme": "dark"}
defaults | saved
{**defaults, **saved}
# Both give: {'theme': 'dark', 'lang': 'en'}
The | operator needs Python 3.9 or later, while the ** version works on older versions too. Both create a new dictionary. By contrast, defaults.update(saved) and defaults |= saved change defaults in place.
Comprehensions and sorting
Dictionary comprehensions build a new dictionary in a single readable line, just as list comprehensions do for lists:
prices = {"pen": 10, "book": 250, "bag": 900}
{k: v for k, v in prices.items() if v > 100}
# {'book': 250, 'bag': 900}
To sort by value, pass a key function to sorted(). To find the key with the largest value, use max():
sorted(prices.items(), key=lambda item: item[1], reverse=True)
# [('bag', 900), ('book', 250), ('pen', 10)]
max(prices, key=prices.get)
# 'bag'
A dictionary has no sort() method, and sorted() returns a list of pairs. Wrap the result in dict() if you want a dictionary in that order.
Counting and grouping patterns
Two tasks come up constantly. The first is counting, which you can do using get() with a default of zero:
words = "to be or not to be".split()
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)
# {'to': 2, 'be': 2, 'or': 1, 'not': 1}
The standard library makes this even shorter with Counter:
from collections import Counter
Counter(words).most_common(2)
# [('to', 2), ('be', 2)]
The second is grouping, and defaultdict is ideal here because it creates the missing value for you:
from collections import defaultdict
orders = [("Asha", 1200), ("Ben", 300), ("Asha", 800)]
by_customer = defaultdict(list)
for name, total in orders:
by_customer[name].append(total)
print(dict(by_customer))
# {'Asha': [1200, 800], 'Ben': [300]}
Without it, you would need to check whether each key exists before appending. These same patterns work well for aggregating API data or log lines. When a dictionary is empty or a key is missing, our guide to Python exception handling shows how to deal with the resulting KeyError.
Nested data and the copy trap
JSON maps directly to nested dictionaries and lists, so you will often chain keys. Chaining get() keeps this safe when a level may be missing:
response = {"user": {"profile": {"city": "Pune"}}}
response["user"]["profile"]["city"] # 'Pune'
response.get("user", {}).get("settings", {}).get("theme") # None
Be careful when copying nested data. copy() duplicates only the top level, so inner dictionaries stay shared with the original:
settings = {"db": {"host": "localhost"}}
shallow = settings.copy()
shallow["db"]["host"] = "prod"
print(settings["db"]["host"])
# prod
Changing the copy changed the original, which can be a nasty bug. Use copy.deepcopy(settings) to get a fully independent copy.
What can be a dictionary key?
Keys must be hashable, which generally means immutable. Strings, numbers and tuples of immutable values all work. Lists and dictionaries do not:
{["a"]: 1}
# TypeError: unhashable type: 'list'
The reason is that a key’s hash decides where its value is stored. If a key could change, Python would no longer be able to find it. If you need a compound key, use a tuple, such as {("Pune", 2026): 120}.
Common mistakes
- Using square brackets on optional keys. Reach for
get()when a key might be absent. - Assuming
get()means the key exists.Nonecould mean missing, or a storedNone. Useinwhen the difference matters. - Using a mutable default.
dict.fromkeys(names, [])gives every key the very same list. Use a comprehension instead. - Changing size during iteration. Loop over a copy of the keys.
- Using a dict for fixed, known fields. A
dataclasscan be clearer, and it catches typos in field names.
Final thoughts
Dictionaries reward a little practice. Remember get() for optional keys, items() for loops, and Counter and defaultdict for the two most common jobs. To practise, take a small JSON file and write code to count and group its records. If you are new to the language, begin with our Python tutorial for beginners.
Frequently asked questions
What is a dictionary in Python?
A dictionary is a built-in collection that stores key and value pairs. You look up a value by its key, which is fast even with a large amount of data.
Are Python dictionaries ordered?
Yes. Since Python 3.7, dictionaries are guaranteed to keep the order in which keys were inserted. Updating an existing key does not change its position.
What is the difference between get() and square brackets?
Square brackets raise KeyError when a key is missing. get() returns None, or a default you provide, so it suits keys that are optional.
Can a dictionary have duplicate keys?
No. Each key is unique. Assigning to an existing key replaces its value, and if a literal repeats a key, the last value wins.
How do I merge two dictionaries in Python?
Use a | b on Python 3.9 or later, or {**a, **b} on older versions. Both build a new dictionary, and values from b win when keys clash.
Keep learning
Need help with testing, WordPress or SEO?
We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.