dict: ключ и значение

Цели урока
  • Создавать dict и читать значение по ключу
  • Добавлять, обновлять и удалять пары ключ-значение
  • Использовать get со значением по умолчанию, чтобы избежать KeyError
  • Перебирать словарь через keys, values и items и работать со списком словарей

Что мы изучим?

dict (Dictionary, словарь) — структура, которая хранит пары из ключа и значения. Вместо доступа по позиции ([0]) обращаются по имени (["name"]).

Зачем это нужно?

В tuple ("Dana", 24, "Haifa") нужно помнить, что позиция 1 — это возраст. При 15 полях это рецепт для багов. dict решает эту проблему:

student = {"name": "Dana", "age": 24, "city": "Haifa"}
student["age"]   # clear and readable

dict — пожалуй, самая важная структура на ближайшие недели:

  • JSON (завтра): формат, в котором сохраняют файлы и пересылают информацию по сети. В Python он превращается в dict.
  • Темы бэкенда и баз данных: каждый запрос и ответ в API — по сути dict. {"id": 7, "name": "Dana"}.
  • Любую «запись» — пользователя, товар или заказ — удобно представлять как dict.

Объяснение с основ

Что такое dict?

  • Каждый элемент — пара: ключ (Key) → значение (Value).
  • Уникальные ключи — двух одинаковых ключей нет. Присваивание существующему ключу заменяет значение.
  • Изменяемый (mutable).
  • Сохраняет порядок вставки (начиная с Python 3.7).
  • Ключ обычно str или int. Значение может быть любым: число, текст, список и даже другой dict.
Аналогия: словарь / телефонная книга

В настоящем словаре вы ищете слово (ключ) и получаете определение (значение). Вы не ищете «определение на позиции 5000 в книге». В телефонной книге: имя → номер. Два человека не могут появиться под абсолютно одинаковым именем как отдельные записи. Новая запись с тем же именем заменяет предыдущую.

Синтаксис

# create
empty = {}
student = {"name": "Dana", "age": 24}

# read
student["name"]                 # "Dana" - KeyError if the key does not exist
student.get("email")            # None - no error if missing
student.get("email", "N/A")     # "N/A" - default value if missing

# add / update (same syntax!)
student["email"] = "dana@mail.com"   # new key -> added
student["age"] = 25                  # existing key -> updated

# delete
del student["email"]            # delete a key (KeyError if missing)
age = student.pop("age")        # delete and return the value

# check
"name" in student               # True - checks KEYS, not values
len(student)                    # number of key-value pairs

# iterate
for key in student:             # keys
for value in student.values():  # values
for key, value in student.items():   # pairs (tuples!)

Первый пример кода

week-2/student_dict.py:

student = {"name": "Dana", "age": 24, "city": "Haifa"}

print("Name:", student["name"])
print("Age:", student["age"])

student["age"] = 25
student["email"] = "dana@mail.com"
print("Updated:", student)

del student["city"]
print("After delete:", student)

print("Has email?", "email" in student)
print("Phone:", student.get("phone", "not provided"))

Вывод:

Name: Dana
Age: 24
Updated: {'name': 'Dana', 'age': 25, 'city': 'Haifa', 'email': 'dana@mail.com'}
After delete: {'name': 'Dana', 'age': 25, 'email': 'dana@mail.com'}
Has email? True
Phone: not provided

Разбор кода построчно

СтрокаОбъяснение
{"name": "Dana", ...}Ключ, двоеточие, значение. Пары разделяются запятыми.
student["name"]Чтение по ключу: значение "Dana".
student["age"] = 25Ключ существует, поэтому значение обновляется.
student["email"] = "..."Ключа нет, поэтому он добавляется (в конец). Абсолютно тот же синтаксис!
del student["city"]Удаление всей пары.
"email" in studentПроверяет, существует ли ключ.
student.get("phone", "not provided")Ключа нет, поэтому возвращается значение по умолчанию, без ошибки.

Ещё один пример

Список словарей — самая распространённая структура для «настоящих» данных (таблица: каждый dict — строка). А также подсчёт с помощью dict. week-2/products.py:

products = [
    {"id": 1, "name": "Laptop", "price": 3500, "category": "electronics"},
    {"id": 2, "name": "Mouse", "price": 80, "category": "electronics"},
    {"id": 3, "name": "Desk", "price": 900, "category": "furniture"},
    {"id": 4, "name": "Chair", "price": 450, "category": "furniture"},
]

print("--- Catalog ---")
for product in products:
    print(product["id"], product["name"], product["price"], "NIS")

expensive = []
for product in products:
    if product["price"] > 500:
        expensive.append(product["name"])
print("Over 500 NIS:", expensive)

count_by_category = {}
for product in products:
    category = product["category"]
    count_by_category[category] = count_by_category.get(category, 0) + 1
print("Count by category:", count_by_category)

for category, count in count_by_category.items():
    print(category, "->", count, "products")

Вывод:

--- Catalog ---
1 Laptop 3500 NIS
2 Mouse 80 NIS
3 Desk 900 NIS
4 Chair 450 NIS
Over 500 NIS: ['Laptop', 'Desk']
Count by category: {'electronics': 2, 'furniture': 2}
electronics -> 2 products
furniture -> 2 products

Разбор:

  • products — список, каждый элемент которого — dict. product["price"] = цена текущего товара в цикле.
  • Шаблон подсчёта: count_by_category.get(category, 0) + 1. Если категории ещё нет в словаре, get возвращает 0, а затем 0+1 = 1. Если она уже есть, вы получаете текущее количество и прибавляете 1. Этот шаблон встречается снова и снова.
  • .items() — возвращает пары (key, value) в виде tuple, а for category, count in распаковывает их (распаковка из предыдущего урока).

dict внутри dict

Значение само может быть dict:

user = {
    "name": "Dana",
    "address": {"city": "Haifa", "street": "Herzl 10"},
    "hobbies": ["chess", "running"],
}
print(user["address"]["city"])   # Haifa
print(user["hobbies"][0])        # chess

Обращаются «слой за слоем»: user["address"] возвращает dict, а у него берут ["city"]. Это именно структура JSON.

Типичная ошибка

Ошибка 1: KeyError для несуществующего ключа.

student = {"name": "Dana"}
print(student["age"])
KeyError: 'age'

Исправление: student.get("age") (возвращает None), student.get("age", 0) (значение по умолчанию) или сначала проверить через if "age" in student:.

Ошибка 2: искать значение через in.

student = {"name": "Dana"}
print("Dana" in student)
False

in для dict проверяет ключи. Чтобы проверить значения: "Dana" in student.values().

Ошибка 3: опечатка в ключе, которая создаёт новый ключ вместо обновления.

student = {"name": "Dana", "age": 24}
student["Age"] = 25
print(student)
{'name': 'Dana', 'age': 24, 'Age': 25}

Никакой ошибки! Просто добавился новый ключ "Age", а "age" остался 24. Ключи чувствительны к регистру. Это тихая Logical Error, поэтому держите имена ключей единообразными (snake_case).

Ошибка 4: список в качестве ключа.

locations = {[32, 35]: "Haifa"}
TypeError: unhashable type: 'list'

Ключ должен быть неизменяемым (Immutable). Если нужен ключ из нескольких значений, используйте tuple: {(32, 35): "Haifa"}. (Связь с неизменяемостью объясним в следующем уроке.)

Практика

Упражнения опираются только на то, что пройдено в этом уроке и в предыдущих. Сначала попробуйте сами, и только потом открывайте решение.

Упражнение 1: Карточка студента

Создайте словарь student с именем "Dana", возрастом 24 и городом "Haifa". Затем:

  1. Обновите возраст на 25.
  2. Добавьте email "dana@mail.com".
  3. Удалите город.
  4. Напечатайте словарь, а также телефон с помощью get со значением по умолчанию "none".

Ожидаемый вывод:

{'name': 'Dana', 'age': 25, 'email': 'dana@mail.com'}
none
Показать решение
student = {"name": "Dana", "age": 24, "city": "Haifa"}

student["age"] = 25
student["email"] = "dana@mail.com"
del student["city"]

print(student)
print(student.get("phone", "none"))
{'name': 'Dana', 'age': 25, 'email': 'dana@mail.com'}
none

student["phone"] выбросил бы KeyError. get возвращает значение по умолчанию, когда ключа нет.

Упражнение 2: Подсчёт слов

Напишите функцию count_words(text), которая возвращает словарь: каждое слово (в нижнем регистре) → сколько раз оно встречается.

counts = count_words("the cat and the dog and THE bird")
print(counts)

Ожидаемый вывод:

{'the': 3, 'cat': 1, 'and': 2, 'dog': 1, 'bird': 1}
Показать решение
def count_words(text):
    counts = {}
    for word in text.lower().split():
        counts[word] = counts.get(word, 0) + 1
    return counts


counts = count_words("the cat and the dog and THE bird")
print(counts)
{'the': 3, 'cat': 1, 'and': 2, 'dog': 1, 'bird': 1}

counts.get(word, 0) возвращает 0 для слова, которое ещё не посчитано, — так не нужен отдельный if для нового слова.

Упражнение 3: Список словарей

products = [
    {"name": "Laptop", "price": 3500, "category": "electronics"},
    {"name": "Mouse", "price": 80, "category": "electronics"},
    {"name": "Desk", "price": 900, "category": "furniture"},
]
  1. Напечатайте названия товаров, цена которых ниже 1000.
  2. Постройте словарь, который считает, сколько товаров в каждой категории, и напечатайте его.

Ожидаемый вывод:

Mouse
Desk
{'electronics': 2, 'furniture': 1}
Показать решение
products = [
    {"name": "Laptop", "price": 3500, "category": "electronics"},
    {"name": "Mouse", "price": 80, "category": "electronics"},
    {"name": "Desk", "price": 900, "category": "furniture"},
]

for product in products:
    if product["price"] < 1000:
        print(product["name"])

counts = {}
for product in products:
    category = product["category"]
    counts[category] = counts.get(category, 0) + 1
print(counts)
Mouse
Desk
{'electronics': 2, 'furniture': 1}

Упражнение 4: Перебор словаря

stock = {"apple": 12, "banana": 0, "cherry": 5}

Переберите словарь через items() и напечатайте каждый товар и его количество. Для товара с количеством 0 напечатайте out of stock.

Ожидаемый вывод:

apple -> 12
banana -> out of stock
cherry -> 5
Показать решение
stock = {"apple": 12, "banana": 0, "cherry": 5}

for name, quantity in stock.items():
    if quantity == 0:
        print(name, "->", "out of stock")
    else:
        print(name, "->", quantity)
apple -> 12
banana -> out of stock
cherry -> 5

items() возвращает пары из ключа и значения, а распаковка в цикле разделяет каждую пару на две переменные.

Упражнение 5: Вложенный словарь

user = {
    "name": "Dana",
    "address": {"city": "Haifa", "street": "Herzl"},
    "hobbies": ["chess", "running"],
}

Напечатайте город, первое хобби и добавьте хобби "music". В конце напечатайте список хобби.

Ожидаемый вывод:

Haifa
chess
['chess', 'running', 'music']
Показать решение
user = {
    "name": "Dana",
    "address": {"city": "Haifa", "street": "Herzl"},
    "hobbies": ["chess", "running"],
}

print(user["address"]["city"])
print(user["hobbies"][0])
user["hobbies"].append("music")
print(user["hobbies"])
Haifa
chess
['chess', 'running', 'music']

Каждые [ ] уходят на уровень глубже: user["address"] — словарь, а ["city"] достаёт из него значение. Это именно структура JSON.

Итоги

  • dict {key: value} — доступ по ключу, а не по позиции. Ключи уникальны.
  • d[key] = value — добавляет, если ключ новый, обновляет, если он существует.
  • d[key] выбрасывает KeyError для отсутствующего ключа. d.get(key, default) не выбрасывает.
  • in проверяет ключи. Перебор: keys(), values(), items() (пары).
  • Список словарей = таблица. dict внутри dict = JSON.
  • Шаблон подсчёта: counts[x] = counts.get(x, 0) + 1.

Официальная документация