Dictionaries in Python

💡 Big Idea

A dictionary is a collection that stores data as key–value pairs. Instead of accessing items by their position (index 0, index 1…), you access them by a meaningful name — a key. This makes dictionaries perfect for representing real-world objects: a spaceship has a name, a crew count, a fuel level, a hull rating. A list could store those four values, but a dictionary gives each one a label so you never have to remember which number means what.

1   Why Do We Need Dictionaries?

Suppose you are writing a program to track data about a spaceship. Using a list, you might write:

ship = ["Persephone", 12, 87.5, "operational"]

This works — but it is fragile. To get the fuel level, you have to remember that it is at index 2. Reorder the list and every piece of code that reads it breaks. Add a new field in the middle and all subsequent indices shift.

A dictionary solves this by giving each value a name:

ship = {
    "name":   "Persephone",
    "crew":   12,
    "fuel":   87.5,
    "status": "operational"
}

Now ship["fuel"] always means fuel level — no matter what order the fields are in, no matter how many new fields you add.

🔍 Analogy: A Ship's Log

Imagine a physical logbook for a starship. Each entry has a labelled field: "Vessel name:", "Crew aboard:", "Fuel reserves:", "Mission status:". You do not look up the fuel level by counting to the third line — you look for the label "Fuel reserves:". A Python dictionary is this logbook: labelled fields you look up by name, not by position.

2   Creating a Dictionary

A dictionary is written with curly braces { }. Each entry is a key–value pair written as key: value, with pairs separated by commas.

ship = {
    "name":        "Persephone",
    "class":       "Firefly-class transport",
    "crew":        12,
    "fuel":        87.5,
    "operational": True
}

2.1   The Vocabulary of Dictionaries

TermWhat it meansExample
DictionaryAn unordered (in Python 3.7+ insertion-ordered) collection of key–value pairs.{"name": "Persephone", "crew": 12}
KeyThe label used to look up a value. Must be unique and immutable (usually a string or integer)."name", "crew"
ValueThe data stored under a key. Can be any type — string, int, float, bool, list, even another dictionary."Persephone", 12
Key–value pairOne entry in the dictionary: a key and its associated value together."crew": 12
LookupRetrieving a value by providing its key.ship["crew"]12

2.2   Keys Must Be Unique

A dictionary cannot have two entries with the same key. If you define the same key twice, the second value silently overwrites the first — no error is raised:

ship = {
    "fuel": 87.5,
    "fuel": 42.0     # overwrites the first entry
}
print(ship["fuel"])  # 42.0
⚠️ Keys must be immutable

Keys can be strings, integers, floats, or tuples — anything that cannot be changed after creation. They cannot be lists or other dictionaries (which are mutable). In practice, strings are by far the most common key type. Values, on the other hand, can be anything at all — including lists, other dictionaries, or None.

3   Accessing Values

3.1   Square Bracket Notation

Access a value by putting its key in square brackets, just like a list index — except you use a key name instead of a number:

ship = {
    "name":  "Persephone",
    "crew":  12,
    "fuel":  87.5
}

print(ship["name"])    # Persephone
print(ship["crew"])    # 12
print(ship["fuel"])    # 87.5
⚠️ KeyError — the dictionary equivalent of IndexError

If you ask for a key that does not exist — ship["weapons"] when there is no "weapons" key — Python raises a KeyError. This is the dictionary equivalent of an IndexError. Always make sure the key exists before accessing it, or use the safe .get() method described below.

3.2   The Safe Way: .get()

.get(key) returns the value for a key if it exists, or None if it does not — without raising an error. You can also provide a custom default value as a second argument:

ship = {"name": "Persephone", "crew": 12, "fuel": 87.5}

print(ship.get("crew"))              # 12  — key exists
print(ship.get("weapons"))           # None — key missing, no error
print(ship.get("weapons", "none"))   # none — custom default
💜 When to use [ ] vs .get()

Use square brackets [ ] when you are certain the key exists — for example, when you just created the dictionary yourself. Use .get() when the key might be absent — for example, when reading user-supplied data or merging dictionaries from different sources. Crashing with a KeyError because of a missing key is one of the most common dictionary bugs.

✅ Check Your Understanding
  1. What is the difference between accessing a list item and accessing a dictionary value?
  2. Given this dictionary:

    planet = {"name": "Kepler-22b", "distance_ly": 620, "habitable": True}

    What is the value of planet["distance_ly"]? What happens if you try planet["moons"]?

  3. Rewrite the unsafe access ship["shields"] using .get() so that it returns 0 if the key is missing.
  4. Why must dictionary keys be immutable? Give one example of a valid key and one example of an invalid key.

4   Adding and Modifying Entries

Dictionaries are mutable — you can add new key–value pairs and change existing values after creation. The syntax for both is the same: assignment using square brackets.

4.1   Modifying an Existing Value

ship = {"name": "Persephone", "fuel": 87.5, "crew": 12}

ship["fuel"] = 62.0          # fuel has dropped after a jump
ship["crew"] = 11            # one crew member disembarked
print(ship)
# {'name': 'Persephone', 'fuel': 62.0, 'crew': 11}

4.2   Adding a New Key–Value Pair

If the key does not already exist, assignment creates it:

ship["shields"] = 95
ship["location"] = "Sector 7-G"
print(ship)
# {'name': 'Persephone', 'fuel': 62.0, 'crew': 11, 'shields': 95, 'location': 'Sector 7-G'}
🔍 One operation, two meanings

ship["fuel"] = 62.0 — Python checks: does the key "fuel" already exist? Yes → update it. No → create it. The same syntax handles both cases. This is intentional: Python keeps the operation simple and lets you focus on the data rather than whether a key is new or existing.

4.3   Updating Multiple Values: .update()

.update() merges another dictionary into the current one. Existing keys are updated; new keys are added:

ship = {"name": "Persephone", "fuel": 62.0, "crew": 11}

ship.update({"fuel": 100.0, "shields": 95, "location": "Starbase Omega"})
print(ship)
# {'name': 'Persephone', 'fuel': 100.0, 'crew': 11, 'shields': 95, 'location': 'Starbase Omega'}

5   Removing Entries

Method / KeywordWhat it doesReturns
del ship[key]Removes the entry with that key. Raises KeyError if missing.Nothing.
ship.pop(key)Removes the entry and returns its value. Raises KeyError if missing.The removed value.
ship.pop(key, default)Removes the entry if it exists. Returns default if the key is missing — no error.The removed value, or default.
ship.clear()Removes all entries, leaving an empty dictionary.Nothing.
ship = {
    "name":     "Persephone",
    "fuel":     100.0,
    "shields":  95,
    "location": "Starbase Omega"
}

del ship["location"]                         # no longer tracking location
print(ship)

old_shields = ship.pop("shields")            # remove and capture the old value
print("Removed shields rating:", old_shields)

removed = ship.pop("weapons", "not fitted")  # safe remove — key may not exist
print("Weapons:", removed)
✅ Check Your Understanding
  1. What is the difference between del ship["key"] and ship.pop("key")?
  2. Trace this code. What does ship contain after each line?

    ship = {"name": "Argo", "crew": 50, "fuel": 75.0, "shields": 80}
    ship["fuel"] = 90.0
    ship["weapons"] = "plasma cannons"
    del ship["shields"]
    rating = ship.pop("crew")
    print(ship)
    print(rating)
  3. Why is ship.pop("weapons", None) safer than del ship["weapons"] when you are not sure the key exists?

6   Testing for Membership

The in operator checks whether a key exists in a dictionary — the same operator you used for strings and lists in earlier articles. It always tests keys, never values.

ship = {"name": "Persephone", "fuel": 87.5, "crew": 12}

print("fuel" in ship)        # True  — the key "fuel" exists
print("weapons" in ship)     # False — no such key
print("not" in ship)         # False
print("fuel" not in ship)    # False

This is the correct way to guard against a KeyError before accessing a key you are not sure about:

if "shields" in ship:
    print("Shield strength:", ship["shields"])
else:
    print("No shield data available.")
⚠️ in tests keys, not values

"Persephone" in ship returns False — even though "Persephone" is a value in the dictionary. To check whether a value exists, you need "Persephone" in ship.values(). More on .values() in the next section.

✅ Check Your Understanding
  1. What does in test when used with a dictionary — keys or values?
  2. Given ship = {"name": "Argo", "crew": 50, "fuel": 75.0}, predict True or False for each:

    "crew" in ship
    50 in ship
    "weapons" not in ship
    "fuel" in ship
  3. Write a safe block of code that checks whether a key "warp_drive" exists in ship before printing its value.

7   Iterating Through a Dictionary

Python gives you three views of a dictionary for iteration. Each is a live view of the dictionary's contents — not a copy — and each lets you loop over a different aspect of the data.

MethodWhat you getUse when…
ship.keys()All the keys.You only need to know what fields exist.
ship.values()All the values.You need to process every value but do not need the keys.
ship.items()All key–value pairs as tuples: (key, value).You need both the key and the value together — the most common case.

7.1   Iterating Over Keys

ship = {
    "name":    "Persephone",
    "class":   "Firefly",
    "crew":    12,
    "fuel":    87.5,
    "shields": 95
}

for key in ship.keys():
    print(key)

Output:

name
class
crew
fuel
shields
💜 Iterating over a dictionary directly gives you keys

Writing for key in ship: is identical to for key in ship.keys():. Iterating over a dictionary without specifying a view always iterates over keys. Using .keys() explicitly makes your intent clearer, which is good style — especially for beginners.

7.2   Iterating Over Values

for value in ship.values():
    print(value)

Output:

Persephone
Firefly
12
87.5
95

7.3   Iterating Over Key–Value Pairs

This is the most useful form. .items() gives you both the key and the value together, which you unpack into two variables:

for key, value in ship.items():
    print(f"  {key:<10} : {value}")

Output:

  name       : Persephone
  class      : Firefly
  crew       : 12
  fuel       : 87.5
  shields    : 95

7.4   Practical Iteration — Building a Status Report

fleet = [
    {"name": "Persephone", "crew": 12, "fuel": 87.5, "shields": 95},
    {"name": "Icarus IV",  "crew": 8,  "fuel": 43.0, "shields": 60},
    {"name": "Calypso",    "crew": 20, "fuel": 91.0, "shields": 88},
]

print("=== Fleet Status ===")
for ship in fleet:
    status = "⚠ LOW FUEL" if ship["fuel"] < 50 else "OK"
    print(f"  {ship['name']:<15} crew: {ship['crew']:<4} "
          f"fuel: {ship['fuel']:<6} shields: {ship['shields']}  {status}")

Output:

=== Fleet Status ===
  Persephone      crew: 12   fuel: 87.5  shields: 95  OK
  Icarus IV       crew: 8    fuel: 43.0  shields: 60  ⚠ LOW FUEL
  Calypso         crew: 20   fuel: 91.0  shields: 88  OK
✅ Check Your Understanding
  1. What is the difference between .keys(), .values(), and .items()?
  2. Write a loop that prints every key–value pair in this dictionary, formatted as "name → Persephone":

    ship = {"name": "Persephone", "crew": 12, "fuel": 87.5}
  3. Given the fleet list above, write a loop that prints only the names of ships with shields below 70.
  4. A student writes for item in ship: and expects to get both keys and values. What do they actually get, and how do they fix it?

8   Other Useful Dictionary Methods

MethodWhat it doesExampleResult
len(ship)Returns the number of key–value pairs.len({"a": 1, "b": 2})2
ship.copy()Returns a shallow copy. Same rules as list copying — assignment does not copy.backup = ship.copy()Independent copy.
ship.setdefault(key, default)Returns the value for key if it exists. If not, inserts key with default and returns default.ship.setdefault("shields", 100)100 added if missing.
dict()Creates a dictionary from keyword arguments or an iterable of pairs.dict(name="Argo", crew=50){"name": "Argo", "crew": 50}

8.1   Copying a Dictionary

Just like lists, assigning a dictionary to a new variable does not create a copy — both variables point to the same object:

original = {"name": "Persephone", "fuel": 87.5}
alias = original                # NOT a copy

alias["fuel"] = 0.0
print(original["fuel"])         # 0.0 — the original was changed too!

Use .copy() to get a true independent copy:

original = {"name": "Persephone", "fuel": 87.5}
backup = original.copy()

backup["fuel"] = 0.0
print(original["fuel"])         # 87.5 — untouched

9   Richer Data: Dictionaries Containing Lists

Values in a dictionary can be any type — including lists. This is how you naturally represent an object with multiple items in one of its fields:

ship = {
    "name":      "Persephone",
    "crew":      12,
    "fuel":      87.5,
    "cargo":     ["food supplies", "medical kits", "engine parts"],
    "visited":   ["Proxima Station", "New Shanghai", "Titan Outpost"],
}

# Access the whole list
print(ship["cargo"])
# ['food supplies', 'medical kits', 'engine parts']

# Access one item from the list
print(ship["cargo"][0])
# food supplies

# Add to the list
ship["cargo"].append("spare hull plating")
print(len(ship["cargo"]))       # 4

# Check for an item in the list
if "medical kits" in ship["cargo"]:
    print("Medical supplies confirmed aboard.")

The notation ship["cargo"][0] reads left to right: first retrieve the value stored under "cargo" (which is a list), then access index 0 of that list.

✅ Check Your Understanding
  1. Given the ship dictionary above, what does ship["visited"][-1] return?
  2. Write a line that adds "Kepler Colony" to the "visited" list inside ship.
  3. Write a loop that prints each item in ship["cargo"] on its own line, numbered from 1.

10   Putting It All Together: The Fleet Management System

Here is a complete program that uses almost everything from this article. The data is a fleet of ships stored as a list of dictionaries. Read it carefully and trace a few of the function calls before running it.

FUEL_WARNING = 50.0         # global constant — low fuel threshold
SHIELD_WARNING = 70         # global constant — low shield threshold

# ── Fleet data ────────────────────────────────────────────────────
fleet = [
    {
        "name":    "Persephone",
        "class":   "Firefly",
        "crew":    12,
        "fuel":    87.5,
        "shields": 95,
        "cargo":   ["food supplies", "medical kits", "engine parts"],
    },
    {
        "name":    "Icarus IV",
        "class":   "Dreadnought",
        "crew":    8,
        "fuel":    43.0,
        "shields": 60,
        "cargo":   ["weapons cache", "fuel cells"],
    },
    {
        "name":    "Calypso",
        "class":   "Science Vessel",
        "crew":    20,
        "fuel":    91.0,
        "shields": 88,
        "cargo":   ["research equipment", "specimens", "data drives"],
    },
]


# ── Functions ─────────────────────────────────────────────────────

def get_warnings(ship):
    """Returns a list of warning strings for a given ship dict."""
    warnings = []
    if ship["fuel"] < FUEL_WARNING:
        warnings.append(f"LOW FUEL ({ship['fuel']}%)")
    if ship["shields"] < SHIELD_WARNING:
        warnings.append(f"LOW SHIELDS ({ship['shields']}%)")
    return warnings


def print_ship_summary(ship):
    """Prints a one-line summary for a ship."""
    warnings = get_warnings(ship)
    flag = "  ⚠ " + ", ".join(warnings) if warnings else ""
    print(f"  {ship['name']:<16} [{ship['class']}]  "
          f"crew: {ship['crew']:<4} "
          f"fuel: {ship['fuel']:<6} "
          f"shields: {ship['shields']}{flag}")


def find_ship(fleet, name):
    """Returns the ship dict with the given name, or None if not found."""
    for ship in fleet:
        if ship["name"].lower() == name.lower():
            return ship
    return None


def refuel(ship, amount):
    """Adds fuel to a ship, capped at 100.0. Returns the amount actually added."""
    space = 100.0 - ship["fuel"]
    added = min(amount, space)
    ship["fuel"] = round(ship["fuel"] + added, 1)
    return added


def fleet_cargo_contains(fleet, item):
    """Returns a list of ship names whose cargo contains the given item."""
    carrying = []
    for ship in fleet:
        if item in ship["cargo"]:
            carrying.append(ship["name"])
    return carrying


# ── Main program ──────────────────────────────────────────────────

print("╔══════════════════════════════╗")
print("║   STARFLEET OPERATIONS HQ   ║")
print("╚══════════════════════════════╝\n")

# Fleet overview
print("── Fleet Status ─────────────────────────────")
for ship in fleet:
    print_ship_summary(ship)

# Find and refuel a specific ship
print("\n── Refuelling Operation ─────────────────────")
target = find_ship(fleet, "Icarus IV")
if target:
    added = refuel(target, 70.0)
    print(f"  Transferred {added}% fuel to {target['name']}.")
    print(f"  New fuel level: {target['fuel']}%")

# Check cargo across the fleet
print("\n── Cargo Search ─────────────────────────────")
search_item = "medical kits"
ships_with_item = fleet_cargo_contains(fleet, search_item)
if ships_with_item:
    print(f"  '{search_item}' found aboard: {', '.join(ships_with_item)}")
else:
    print(f"  '{search_item}' not found in any ship's cargo.")

# Print all fields of one ship using .items()
print("\n── Detailed Report: Calypso ─────────────────")
calypso = find_ship(fleet, "Calypso")
for key, value in calypso.items():
    print(f"  {key:<10} : {value}")

Things to notice:

  • get_warnings() iterates over nothing — it just accesses specific keys directly, using in and comparisons.
  • find_ship() loops over the fleet list, checking ship["name"] for each dictionary.
  • fleet_cargo_contains() checks item in ship["cargo"] — the in operator working on a list that is itself a dictionary value.
  • The final loop uses .items() to print every key–value pair of one ship's dictionary — a useful debugging technique.

11   Final Check: The Big Picture

💡 Big Idea

A dictionary stores data as key–value pairs, accessed by meaningful names rather than by position. Access values with [ ] or the safer .get(). Add and modify entries with assignment; remove with del or .pop(). Test for key membership with in. Iterate with .keys(), .values(), or .items() — the last being the most useful. Assigning a dictionary to a new variable does not copy it. Dictionaries can hold any type of value, including lists and other dictionaries, making them the natural structure for representing real-world objects.

✅ Final Check Your Understanding
  1. In your own words: what is the core difference between a list and a dictionary? When would you choose each?
  2. Trace this program completely. Write the state of ship after every line.

    ship = {"name": "Argo", "crew": 50, "fuel": 80.0}
    ship["shields"] = 100
    ship["fuel"] -= 25.0
    old_crew = ship.pop("crew")
    ship.update({"status": "patrol", "fuel": 90.0})
    print(ship)
    print(old_crew)
  3. Write a function called is_combat_ready(ship) that takes a ship dictionary and returns True only if: the ship has a "shields" key, shields are 75 or above, and fuel is 40 or above.
  4. Given a list of ship dictionaries called fleet, write a loop that prints the name of every ship that carries "weapons cache" in its "cargo" list.
  5. This code is supposed to print a backup of ship, then clear the original, but it prints an empty dictionary for both. Why? Fix it.

    ship = {"name": "Persephone", "fuel": 87.5}
    backup = ship
    ship.clear()
    print(backup)

12   Quick-Reference Summary

OperationSyntaxNotes
Created = {"key": value}Curly braces, colon separates key and value, comma between pairs.
Access (unsafe)d["key"]Raises KeyError if the key does not exist.
Access (safe)d.get("key", default)Returns default (or None) if key is missing. No error.
Add / modifyd["key"] = valueCreates key if new; overwrites if existing.
Merge / updated.update(other_dict)Adds new keys; overwrites existing ones.
Remove (no return)del d["key"]Raises KeyError if missing.
Remove (with return)d.pop("key", default)Returns value. Safe with default argument.
Clear alld.clear()Empties the dictionary in place.
Membership (key)"key" in dTests keys only, not values.
Membership (value)value in d.values()Use .values() to search values.
Iterate keysfor k in d.keys():Same as for k in d:
Iterate valuesfor v in d.values():Values only — no keys.
Iterate bothfor k, v in d.items():Most common iteration pattern.
Lengthlen(d)Number of key–value pairs.
Copyd.copy()b = a is NOT a copy — both point to the same dict.