Словари в Python

Допустим, вы храните оценки студентов. Первая мысль — два списка: имена и оценки, и одинаковая позиция связывает их.

Python 3.13
names = ["John", "Mary", "Kate"]
grades = [90, 75, 80]

Чтобы узнать оценку Мэри, сначала нужно найти, на какой позиции она стоит в names, а потом прочитать ту же позицию в grades. Добавляете студента — нельзя забыть дописать значение во второй список. Стоит одному списку сбиться на один элемент, и Мэри получает чужую оценку, а программа об этом даже не сообщит. Связь между именем и оценкой держится только в вашей голове, а не в коде.

Словарь убирает посредника-индекс и связывает имя со значением напрямую.

Python 3.13
grades = {"John": 90, "Mary": 75, "Kate": 80}
print(grades["Mary"])
75

Словарь (dict) — это коллекция пар «ключ — значение». Вы обращаетесь к значению по ключу, а не по числовой позиции.

Телефонная книга устроена так же: вы ищете человека по имени, а не по номеру строки.

Как создать словарь

Чаще всего словарь записывают прямо, в фигурных скобках: пара ключ: значение, пары через запятую.

Python 3.13
# Пустой словарь, который заполним позже
prices = {}

# Словарь сразу с данными
person = {"name": "John", "age": 30, "city": "New York"}
print(person)
{'name': 'John', 'age': 30, 'city': 'New York'}

Если пары уже лежат где-то готовыми, например приходят парами, словарь можно собрать функцией dict().

Python 3.13
pairs = [("name", "Anna"), ("age", 28), ("city", "Berlin")]
person = dict(pairs)
print(person)
{'name': 'Anna', 'age': 28, 'city': 'Berlin'}

Чтение значения: скобки против get

Самый прямой способ получить значение — квадратные скобки с ключом.

Python 3.13
person = {"name": "John", "age": 30}
print(person["name"])
John

Но если ключа в словаре нет, скобки не возвращают пустоту, а останавливают программу с ошибкой KeyError.

Python 3.13
person = {"name": "John", "age": 30}
print(person["phone"])  # KeyError: 'phone' — такого ключа в словаре нет

Это не недостаток, а защита: чаще всего обращение к несуществующему ключу — это опечатка или сломанная логика, и лучше узнать о ней сразу. Скобки уместны, когда вы уверены, что ключ есть.

Когда ключа может и не быть, есть метод get(). Он вернёт None вместо ошибки, а если передать второй аргумент — то его как значение по умолчанию.

Python 3.13
person = {"name": "John", "age": 30}

print(person.get("phone"))
None
print(person.get("phone", "не указан"))
не указан

Отсюда простое правило выбора: скобки — когда отсутствие ключа это ошибка, get() — когда отсутствие это нормальный случай, который вы готовы обработать.

Добавление и изменение

У словаря один и тот же синтаксис и добавляет ключ, и меняет существующий: присваивание по ключу. Если ключа не было — он появится, если был — значение перезапишется.

Python 3.13
person = {"name": "John", "age": 30}

# Ключа "city" не было — он добавляется
person["city"] = "New York"
print(person)
{'name': 'John', 'age': 30, 'city': 'New York'}
# Ключ "age" уже есть — значение заменяется
person["age"] = 31
print(person)
{'name': 'John', 'age': 31, 'city': 'New York'}

Когда нужно внести сразу несколько изменений, удобнее update(): он добавляет новые ключи и перезаписывает совпавшие за один вызов.

Python 3.13
person = {"name": "John", "age": 31}
person.update({"age": 32, "job": "developer"})
print(person)
{'name': 'John', 'age': 32, 'job': 'developer'}

Проверка наличия ключа

Чтобы заранее узнать, есть ли ключ, используют оператор in. Это и есть способ не нарваться на KeyError, когда скобки всё-таки нужны.

Python 3.13
person = {"name": "John", "age": 30}

print("name" in person)
True
print("phone" in person)
False

Удаление

Убрать ключ можно оператором del. Но если ключа нет, del тоже падает с KeyError.

Python 3.13
person = {"name": "John", "age": 30, "job": "developer"}

del person["job"]
print(person)
{'name': 'John', 'age': 30}

Метод pop() удаляет ключ и заодно возвращает его значение — пригодится, когда удаляемое значение ещё нужно. Со вторым аргументом он не падает на отсутствующем ключе, а возвращает значение по умолчанию.

Python 3.13
person = {"name": "John", "age": 30}

age = person.pop("age")
print(age)
30
# Ключа "phone" нет, но второй аргумент спасает от ошибки
phone = person.pop("phone", "не указан")
print(phone)
не указан

Перебор словаря

По словарю можно пройтись циклом. По умолчанию цикл for идёт по ключам, а значение легко достать по ключу.

Python 3.13
grades = {"John": 90, "Mary": 75, "Kate": 80}

for name in grades:
    print(name, ":", grades[name])
John : 90
Mary : 75
Kate : 80

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

Python 3.13
grades = {"John": 90, "Mary": 75, "Kate": 80}

for name, grade in grades.items():
    print(name, ":", grade)
John : 90
Mary : 75
Kate : 80

Есть и парные методы: keys() отдаёт только ключи, values() — только значения. Они нужны, когда вторая половина пары в цикле не используется.

Python 3.13
grades = {"John": 90, "Mary": 75, "Kate": 80}

total = 0
for grade in grades.values():
    total = total + grade
print("Сумма баллов:", total)
Сумма баллов: 245

Частая задача: подсчёт

Словарь хорошо подходит, чтобы что-то посчитать: ключ — это объект, значение — счётчик. Посчитаем, сколько раз встречается каждое слово.

В лоб это выглядит так: для каждого слова проверяем, видели ли мы его раньше, и либо увеличиваем счётчик, либо заводим новый.

Python 3.13
text = "one two one two three"
words = text.split()

counts = {}
for word in words:
    if word in counts:
        counts[word] = counts[word] + 1
    else:
        counts[word] = 1
print(counts)
{'one': 2, 'two': 2, 'three': 1}

Здесь как раз помогает get() со значением по умолчанию: «возьми текущий счётчик, а если слова ещё не было — считай, что был ноль». Условие if/else сворачивается в одну строку.

Python 3.13
text = "one two one two three"
words = text.split()

counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1
print(counts)
{'one': 2, 'two': 2, 'three': 1}

Что годится в ключи

Ключи живут по двум правилам, и оба следуют из того, как словарь устроен внутри.

  • Ключи уникальны. Записать два одинаковых ключа нельзя: второе присваивание просто перезапишет первое. Это логично — иначе по ключу "Mary" было бы непонятно, какое из значений возвращать.
  • Ключ должен быть неизменяемым. Подходят строки, числа, кортежи. Список ключом быть не может: Python находит значение по ключу через его неизменяемое содержимое, а список можно поменять уже после того, как он стал ключом, и значение «потеряется».
Python 3.13
# Список в роли ключа — ошибка
broken = {[1, 2]: "value"}  # TypeError: unhashable type: 'list'

Значения же могут быть любыми: числа, строки, списки и даже другие словари.

Проверка понимания

Что выведет print(person["phone"]), если в словаре person нет ключа "phone"?