A dictionary (often called a dict) is a collection of key/value pairs. Give it a key, and it gives you back the matching value. Dictionaries are the foundation of JSON, the shape of most config files, and the most useful structure in Python after the list.

Creating a dictionary

person: dict[str, str | int] = {
    "name": "Manikandan",
    "age": 30,
    "city": "Chennai",
}

prices: dict[str, float] = {
    "apple": 0.5,
    "banana": 0.3,
    "cherry": 1.2,
}

empty: dict[str, int] = {}

A dictionary:

  • Uses curly braces { } with key: value pairs.
  • Keys must be hashable — strings, numbers, tuples. Never lists.
  • Values can be anything.
  • The type hint dict[K, V] says key type then value type.

Looking up values

Use square brackets with the key:

print(person["name"])    # 'Manikandan'
print(person["age"])     # 30

If the key doesn’t exist, you get a KeyError:

print(person["email"])   # KeyError: 'email'

To look up without crashing, use .get():

print(person.get("email"))               # None
print(person.get("email", "unknown"))    # 'unknown'  (default)

Adding and changing

person: dict[str, str | int] = {"name": "Manikandan", "age": 30}

person["city"] = "Chennai"      # add a new pair
person["age"] = 31              # change an existing value

print(person)
# {'name': 'Manikandan', 'age': 31, 'city': 'Chennai'}

The same syntax adds new keys and updates existing ones.

Removing items

del person["age"]              # raises KeyError if not present
removed = person.pop("city")   # removes and returns the value
print(removed)                  # 'Chennai'

person.clear()                  # empties the dict

Membership — checking for keys

The in operator checks keys, not values:

person: dict[str, str | int] = {"name": "Manikandan", "age": 30}

print("name" in person)             # True
print("Manikandan" in person)       # False — that's a value
print("Manikandan" in person.values())   # True

in person is fast — same complexity as a set lookup.

Looping over a dictionary

prices: dict[str, float] = {"apple": 0.5, "banana": 0.3, "cherry": 1.2}

# keys (default)
for fruit in prices:
    print(fruit)

# values
for price in prices.values():
    print(price)

# pairs — by far the most common
for fruit, price in prices.items():
    print(f"{fruit}: {price}")

.items() returns (key, value) tuples. The two-variable unpacking makes the loop read naturally.

Useful methods

prices: dict[str, float] = {"apple": 0.5, "banana": 0.3}

print(len(prices))           # 2
print(list(prices.keys()))   # ['apple', 'banana']
print(list(prices.values())) # [0.5, 0.3]
print(list(prices.items()))  # [('apple', 0.5), ('banana', 0.3)]

# merge another dict in
prices.update({"cherry": 1.2, "apple": 0.6})
print(prices)
# {'apple': 0.6, 'banana': 0.3, 'cherry': 1.2}

# safe lookup with a default that's also inserted if missing
count: int = prices.setdefault("date", 0)

Dictionaries preserve insertion order

Since Python 3.7, dictionaries remember the order keys were added. This is useful:

config: dict[str, str] = {}
config["host"] = "localhost"
config["port"] = "8080"
config["timeout"] = "30"

for key, value in config.items():
    print(f"{key} = {value}")
host = localhost
port = 8080
timeout = 30

The order matches the order we added the keys. (Old Python 2 and very old Python 3 versions didn’t promise this.)

Nested dictionaries

Values can be any type — including other dictionaries:

people: dict[str, dict[str, int | str]] = {
    "alice": {"age": 30, "city": "Chennai"},
    "bob": {"age": 25, "city": "Mumbai"},
}

print(people["alice"]["city"])   # 'Chennai'

This is exactly the structure of a JSON object. We’ll handle real JSON in Section 10.

A complete example — counting words

A classic problem: count how many times each word appears in some text.

text: str = "the cat sat on the mat the cat slept"
counts: dict[str, int] = {}

for word in text.split():
    if word in counts:
        counts[word] += 1
    else:
        counts[word] = 1

print(counts)
# {'the': 3, 'cat': 2, 'sat': 1, 'on': 1, 'mat': 1, 'slept': 1}

The standard library has a tool for this exact pattern — collections.Counter. We’ll meet it in Section 14.

Dictionary comprehensions — a teaser

Just like list comprehensions, dictionaries have a one-line builder:

squares: dict[int, int] = {n: n * n for n in range(1, 6)}
print(squares)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Covered in the next lesson.

Why dictionaries are central to data science

Almost every structured data format maps onto a dictionary:

  • JSON is dictionaries (and lists).
  • YAML / TOML config files are dictionaries.
  • A pandas row behaves like a dictionary.
  • A model’s hyperparameters are usually a dictionary.

Get comfortable with dictionaries early. You’ll touch them every day.

What’s next

Last lesson of the section — comprehensions, the one-line way to build lists, sets, and dictionaries.

Toggle theme (T)