Home Python Python Dictionaries Explained — Creation, Lookup, and Common Mistakes

Python Dictionaries Explained — Creation, Lookup, and Common Mistakes

In Plain English 🔥
Imagine a real-life dictionary: you look up a word (the key) and instantly find its definition (the value). A Python dictionary works exactly the same way — instead of flipping through pages, Python jumps straight to the answer in microseconds. You could store a person's name as the key and their phone number as the value, or a product name as the key and its price as the value. It's a lookup table, a phonebook, a menu — any time you need to pair a label with a piece of data, a dictionary is your tool.
⚡ Quick Answer
Imagine a real-life dictionary: you look up a word (the key) and instantly find its definition (the value). A Python dictionary works exactly the same way — instead of flipping through pages, Python jumps straight to the answer in microseconds. You could store a person's name as the key and their phone number as the value, or a product name as the key and its price as the value. It's a lookup table, a phonebook, a menu — any time you need to pair a label with a piece of data, a dictionary is your tool.

Every app you've ever used is quietly powered by key-value pairs. When you log into a website, your username is looked up in a giant table to find your password hash. When Spotify loads your profile, it fetches your name, playlist count, and subscription tier all at once — not as a pile of unconnected numbers, but as named, organised data. Python dictionaries are the building block that makes this kind of organised, instant-access data possible in your own code.

Before dictionaries existed in Python (or before programmers reached for them), people stored related data in parallel lists — one list of names, one list of phone numbers, hoping the indexes stayed in sync. This is fragile, confusing, and breaks the moment someone inserts a row in the wrong place. A dictionary solves this by gluing the label and the value together permanently, so they can never drift apart.

By the end of this article you'll know how to create a dictionary from scratch, read and update values safely, loop through it without breaking anything, and avoid the three mistakes that trip up almost every beginner. You'll also know exactly when a dictionary is the right choice — and when it isn't.

Creating Your First Dictionary — and Understanding What You're Actually Building

A dictionary in Python is written with curly braces {}. Inside, you place pairs of items separated by a colon — the thing on the left of the colon is the key, the thing on the right is the value. Each pair is separated from the next by a comma.

Think of it this way: the key is the label on a filing cabinet drawer, and the value is the document inside. You always open the drawer by its label — never by guessing which drawer number it is.

Keys must be unique. You can't have two drawers with the same label — Python would just keep the last one you defined and silently drop the earlier one. Keys must also be immutable (unchangeable), which in practice means they're almost always strings or numbers. Values, on the other hand, can be absolutely anything: a number, a string, a list, even another dictionary.

You can also create an empty dictionary with just {} and fill it in later — useful when you're building data dynamically, like reading lines from a file.

create_dictionary.py · PYTHON
123456789101112131415161718
# A dictionary representing a single student's record
# Keys are strings (labels), values are mixed types
student = {
    "name": "Maria Chen",       # string value
    "age": 21,                  # integer value
    "gpa": 3.85,                # float value
    "is_enrolled": True,        # boolean value
    "courses": ["Math", "CS"]   # list as a value — totally valid!
}

# Print the whole dictionary
print(student)

# Check what type it is
print(type(student))

# Check how many key-value pairs are inside
print("Number of fields:", len(student))
▶ Output
{'name': 'Maria Chen', 'age': 21, 'gpa': 3.85, 'is_enrolled': True, 'courses': ['Math', 'CS']}
<class 'dict'>
Number of fields: 5
🔥
Why Mixed Types Are Fine:Unlike a spreadsheet column (which expects one type), a dictionary's values can be anything. One key can hold a number while the next holds a list. This flexibility is what makes dictionaries perfect for representing real-world objects like users, products, or API responses.

Reading, Adding, and Updating Values — The Three Core Operations You'll Use Every Day

Once your dictionary exists, you need to pull data out of it. The basic way is square bracket notation: student["name"] — think of it as typing the drawer label to pop it open.

But there's a trap with square brackets: if the key doesn't exist, Python throws a KeyError and your program crashes. The safer approach is the .get() method, which returns None by default (or a fallback value you choose) instead of crashing. In production code, .get() is almost always the right choice.

Adding a new key-value pair looks identical to updating an existing one — dictionary["new_key"] = value. If the key already exists, the value gets replaced. If it doesn't exist yet, a new pair is created. Python handles both cases with the same syntax, which keeps things simple.

Deleting a pair uses del dictionary["key"] or the .pop() method. The advantage of .pop() is that it returns the value you removed, so you can use it before it's gone — handy when you're moving data between structures.

read_update_dictionary.py · PYTHON
1234567891011121314151617181920212223242526272829303132
student = {
    "name": "Maria Chen",
    "age": 21,
    "gpa": 3.85
}

# --- READING ---
# Square bracket access — fast, but crashes if key is missing
print(student["name"])   # works fine

# .get() is the safe version — returns None if key doesn't exist
print(student.get("gpa"))           # prints 3.85
print(student.get("major"))         # prints None — no crash!
print(student.get("major", "Undeclared"))  # custom fallback value

# --- ADDING a new key ---
student["major"] = "Computer Science"  # key didn't exist — now it does
print(student)

# --- UPDATING an existing key ---
student["gpa"] = 3.92  # key already exists — value gets replaced
print("Updated GPA:", student["gpa"])

# --- DELETING a key ---
# Option 1: del — removes silently
del student["age"]
print("After del:", student)

# Option 2: .pop() — removes AND returns the value
gpa_at_graduation = student.pop("gpa")
print("Captured GPA before removing:", gpa_at_graduation)
print("Final record:", student)
▶ Output
Maria Chen
3.85
None
Undeclared
{'name': 'Maria Chen', 'age': 21, 'gpa': 3.85, 'major': 'Computer Science'}
Updated GPA: 3.92
After del: {'name': 'Maria Chen', 'gpa': 3.92, 'major': 'Computer Science'}
Captured GPA before removing: 3.92
Final record: {'name': 'Maria Chen', 'major': 'Computer Science'}
⚠️
Watch Out: KeyError CrashesUsing `dict["key"]` on a key that doesn't exist raises `KeyError` and stops your program cold. In any code that handles user input, API responses, or config files — where keys might be absent — always use `.get()` instead. It's one habit that will save you hours of debugging.

Looping Through a Dictionary — Keys, Values, and Both at Once

Looping over a dictionary is something you'll do constantly — printing a report, transforming data, searching for a specific value. Python gives you three clean methods for this, and knowing which one to use when is a mark of a confident Python developer.

Looping with just for key in dictionary gives you the keys only — the drawer labels. From each key you can look up the value inside the loop if you need it.

The .values() method gives you just the values, no keys — useful when you want to sum up prices or check if a specific value exists anywhere.

The real power move is .items(), which gives you both the key and the value as a pair on every iteration. This is what you'll use 80% of the time because you usually need both. Python unpacks the pair into two variables automatically — for key, value in dictionary.items() — and it reads almost like plain English.

Dictionaries in Python 3.7 and later also maintain insertion order, so the loop will always visit pairs in the order you added them.

loop_through_dictionary.py · PYTHON
12345678910111213141516171819202122232425262728293031323334
product_prices = {
    "Laptop": 999.99,
    "Mouse": 29.99,
    "Keyboard": 79.99,
    "Monitor": 349.99
}

# --- Loop 1: Keys only ---
print("=== Product Names ===")
for product_name in product_prices:
    print("-", product_name)  # each iteration gives us one key

# --- Loop 2: Values only ---
print("\n=== All Prices ===")
total = 0
for price in product_prices.values():
    total += price           # accumulate a running total
print(f"Total inventory value: ${total:.2f}")

# --- Loop 3: Keys AND Values together (the most common pattern) ---
print("\n=== Full Price List ===")
for product_name, price in product_prices.items():  # unpacks each pair
    if price > 100:
        label = "Premium"   # flag expensive items
    else:
        label = "Budget"
    print(f"{product_name}: ${price:.2f} ({label})")

# --- Checking if a key exists before accessing it ---
search_term = "Webcam"
if search_term in product_prices:       # 'in' checks keys by default
    print(f"\nFound {search_term}: ${product_prices[search_term]}")
else:
    print(f"\n'{search_term}' is not in our catalogue.")
▶ Output
=== Product Names ===
- Laptop
- Mouse
- Keyboard
- Monitor

=== All Prices ===
Total inventory value: $1459.96

=== Full Price List ===
Laptop: $999.99 (Premium)
Mouse: $29.99 (Budget)
Keyboard: $79.99 (Budget)
Monitor: $349.99 (Premium)

'Webcam' is not in our catalogue.
⚠️
Pro Tip: Use 'in' to Check Keys, Not ValuesThe expression `'key' in my_dict` checks keys only — it's O(1) speed (instant, no matter how big the dictionary). To check values, you'd need `value in my_dict.values()`, which scans every item — much slower on large data. Design your dictionaries so you search by key whenever possible.

Nested Dictionaries — Storing Complex, Real-World Data Structures

Real data is rarely flat. A user doesn't just have a name — they have an address, which itself has a street, city, and postcode. A dictionary can hold another dictionary as a value, letting you model this hierarchy naturally.

This is called a nested dictionary. You access values inside it by chaining square brackets or .get() calls: user["address"]["city"]. Each set of brackets digs one level deeper — like opening a folder inside a folder.

Nested dictionaries are everywhere in professional Python: JSON data from an API is almost always a nested dictionary (Python's json module converts it automatically). Configuration files, database records, and game state are all commonly stored this way.

One thing to watch when working with nested data: if an intermediate key doesn't exist, chaining square brackets will crash on the first missing key before it even tries the inner one. Using .get() at each level prevents this — or you can use a try/except block if the structure is deeply uncertain.

nested_dictionary.py · PYTHON
1234567891011121314151617181920212223242526272829303132333435363738394041
# A dictionary of users — each key is a user ID, each value is another dictionary
user_database = {
    "usr_001": {
        "username": "mariachen",
        "email": "maria@example.com",
        "address": {
            "street": "14 Elm Street",
            "city": "Austin",
            "postcode": "73301"
        },
        "is_premium": True
    },
    "usr_002": {
        "username": "jakethompson",
        "email": "jake@example.com",
        "address": {
            "street": "9 Oak Avenue",
            "city": "Denver",
            "postcode": "80201"
        },
        "is_premium": False
    }
}

# Access a top-level value
print(user_database["usr_001"]["username"])  # chain brackets to go deeper

# Access a deeply nested value
print(user_database["usr_001"]["address"]["city"])

# Safe access with .get() at each level — no crash if a key is missing
user = user_database.get("usr_003", {})          # returns empty dict if not found
city = user.get("address", {}).get("city", "Unknown")
print("City for usr_003:", city)                  # gracefully returns 'Unknown'

# Loop through all users and print a summary
print("\n=== User Summary ===")
for user_id, user_info in user_database.items():
    tier = "Premium" if user_info["is_premium"] else "Free"
    city = user_info["address"]["city"]
    print(f"{user_id} | @{user_info['username']} | {city} | {tier}")
▶ Output
mariachen
Austin
City for usr_003: Unknown

=== User Summary ===
usr_001 | @mariachen | Austin | Premium
usr_002 | @jakethompson | Denver | Free
🔥
Real-World Connection:When your Python code calls a REST API and gets back JSON, Python converts it to a nested dictionary automatically. Knowing how to read and navigate nested dictionaries is a direct, on-the-job skill — not just an exam topic.
Feature / AspectPython DictionaryPython List
How you access dataBy a named key: dict['name']By a position number: list[0]
Order guaranteed?Yes — insertion order kept (Python 3.7+)Yes — always ordered by index
Lookup speedO(1) — instant, regardless of sizeO(n) — slower as the list grows (for searching)
Duplicate keys allowed?No — last assignment wins silentlyYes — duplicate values are fine
Best used whenYou need to label your data (name, price, id)You have a sequence of similar items (scores, names)
Key types allowedImmutable only: strings, numbers, tuplesNot applicable — lists use integer indexes
Memory usageSlightly higher due to hash table overheadLower — compact sequential storage

🎯 Key Takeaways

  • A dictionary stores data as key-value pairs — use it any time your data has labels (name, price, id) rather than just a sequence of items.
  • Always use .get() instead of square brackets when the key might not exist — it prevents KeyError crashes and lets you define a sensible fallback value.
  • Dictionary lookups are O(1) — Python uses a hash table internally, meaning it jumps directly to the value regardless of how many pairs are stored.
  • Nested dictionaries (a dictionary inside a dictionary) are the natural way to model real-world hierarchical data like users, addresses, or JSON API responses.

⚠ Common Mistakes to Avoid

  • Mistake 1: Using a mutable type (like a list) as a dictionary key — You'll get a TypeError: unhashable type: 'list' immediately. Python requires keys to be immutable because it calculates a hash from the key to find the value instantly. Fix it by converting the list to a tuple first: use (1, 2, 3) as your key instead of [1, 2, 3].
  • Mistake 2: Accessing a key that might not exist with square brackets instead of .get() — This raises a KeyError and crashes your program, which is especially painful when processing user input or API data where keys aren't guaranteed. Fix it by switching to .get('key', fallback_value) — it returns None (or your chosen default) instead of crashing.
  • Mistake 3: Modifying a dictionary while iterating over it — Python raises RuntimeError: dictionary changed size during iteration if you add or delete keys inside a for key in dictionary loop. Fix it by iterating over a snapshot of the keys: for key in list(dictionary.keys()) — this creates a static copy of the keys first, so adding or removing from the original dictionary during the loop is safe.

Interview Questions on This Topic

  • QWhat's the difference between dict['key'] and dict.get('key'), and when would you choose one over the other in production code?
  • QDictionaries are described as O(1) for lookups — can you explain why that's the case, and what data structure Python uses under the hood to achieve it?
  • QIf you have two large lists — one of employee names and one of their salaries — and you need to frequently look up a salary by name, how would you restructure this data and why?

Frequently Asked Questions

Can a Python dictionary have duplicate keys?

No — dictionary keys must be unique. If you define the same key twice, Python doesn't raise an error; it silently keeps the last value assigned to that key and discards the earlier one. This is a common source of subtle bugs, so always make sure your keys are distinct.

What types can be used as dictionary keys in Python?

Only immutable (unchangeable) types can be keys — strings, integers, floats, and tuples are all valid. Lists and dictionaries cannot be keys because they can be changed after creation, which would break Python's internal hash table. If you need a sequence as a key, convert it to a tuple first.

Is a Python dictionary ordered? Will my keys always come back in the same order?

Yes, from Python 3.7 onwards dictionaries are guaranteed to maintain insertion order — the order you added the key-value pairs is the order you'll get them back when you loop or print. In Python 3.6 and earlier this was not guaranteed, so if you're on a modern Python version (which you almost certainly are), you can rely on insertion order.

🔥
TheCodeForge Editorial Team Verified Author

Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.

← PreviousTuples in PythonNext →Sets in Python
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged