Что такое словарь в Python
Словарь — это набор пар «ключ: значение» в фигурных скобках, где каждый ключ встречается только один раз. По ключу словарь сразу отдаёт связанное с ним значение, без перебора всех элементов.
ages = {"Аня": 11, "Миша": 12, "Оля": 10}
print(ages["Миша"])
print(len(ages))
print(ages)
12
3
{'Аня': 11, 'Миша': 12, 'Оля': 10}
Слева от двоеточия стоит ключ, справа — значение, пары разделены запятыми. Тип словаря называется dict, пустой словарь создают как {} или dict(). Функция len() считает пары, а не отдельные ключи и значения. Название «словарь» говорит само за себя: как в бумажном словаре вы ищете слово и находите его толкование, так и словарь в Python по ключу находит значение.
Словари в Python часто сравнивают с картотекой: у каждой карточки есть подпись, и нужную находят по ней, не листая остальные. Ключами обычно делают строки или числа, а значением может быть что угодно: число, строка, список и даже другой словарь. Главное ограничение — ключ должен быть неизменяемым, поэтому список ключом стать не может. Начиная с версии 3.7 официальная документация Python гарантирует, что пары хранятся в том порядке, в котором их добавили[1]. В школьной программе по ФОП отдельной темы про словари нет, но в учебных проектах и олимпиадных задачах они встречаются постоянно, потому что заметно сокращают код.
Как прочитать и изменить значение
Чтобы получить значение, пишут имя словаря и ключ в квадратных скобках: ages["Аня"]. Та же запись слева от знака = меняет значение или добавляет новую пару, если такого ключа ещё не было.
stock = {"яблоки": 10, "груши": 4}
stock["груши"] = 7
stock["сливы"] = 12
print(stock)
del stock["яблоки"]
print(stock)
taken = stock.pop("груши")
print(taken, stock)
| Действие | Запись | Что произойдёт |
| Прочитать | d[key] | Вернёт значение или KeyError, если ключа нет |
| Добавить или изменить | d[key] = value | Создаст пару или заменит старое значение |
| Удалить | del d[key] | Удалит пару, KeyError при отсутствии ключа |
| Удалить и получить | d.pop(key) | Удалит пару и вернёт значение |
| Проверить наличие | key in d | True или False, ищет только среди ключей |
| Число пар | len(d) | Количество ключей |
Обратите внимание: присваивание по существующему ключу не создаёт дубль, а заменяет значение. Поэтому в словаре не бывает двух одинаковых ключей — второй просто перезапишет первый. Оператор in проверяет только ключи: "яблоки" in stock — проверка ключа, а не значения. Чтобы найти значение, перебирают values(), но это уже медленный путь: быстрый поиск словари в Python дают только по ключу.
Метод get: значение без ошибки
Метод get возвращает значение по ключу, а если ключа нет — None или значение, указанное вторым аргументом. Это самый простой способ не получить KeyError, когда ключ может отсутствовать.
colors = {"red": "красный", "green": "зелёный"}
print(colors.get("red"))
print(colors.get("blue"))
print(colors.get("blue", "не знаю такого"))
Запись d[key] подходит, когда вы уверены, что ключ есть. Оба способа описаны в официальной документации Python в разделе о структурах данных. Если ключ приходит от пользователя или из данных, где его может не быть, безопаснее d.get(key, запасное_значение). Ещё один вариант — проверить заранее: if key in d:. Что происходит при обращении к отсутствующему ключу и как читать такое сообщение, разобрано на странице об ошибке KeyError.
Перебор словаря: keys, values и items
Цикл for по словарю перебирает ключи, а 3 метода дают разные виды перебора: keys() — ключи, values() — значения, items() — пары ключ и значение сразу.
menu = {"суп": 120, "каша": 80, "компот": 40}
for dish in menu:
print(dish)
print(sum(menu.values()))
for dish, price in menu.items():
print(dish, "—", price, "руб.")
Самый удобный вариант — items(): в заголовке цикла сразу две переменные, и обращаться к menu[dish] не нужно. Метод values() пригодится, когда ключи не важны, например для суммы или максимума. Если нужно вывести пары по алфавиту, переберите sorted(menu) — функция вернёт отсортированный список ключей. Менять набор ключей словаря прямо во время перебора нельзя: Python остановит программу с ошибкой RuntimeError. Как устроен сам цикл, рассказано в уроке про цикл for.
Подсчёт частоты: самый частый приём со словарём
Чтобы посчитать, сколько раз встречается каждый элемент, заводят пустой словарь и для каждого элемента увеличивают его счётчик на 1. Это классическая задача, которую со словарём решают в 3 строки.
text = "кот ест кашу кот спит кот"
freq = {}
for word in text.split():
freq[word] = freq.get(word, 0) + 1
print(freq)
for word in sorted(freq):
print(word, freq[word])
{'кот': 3, 'ест': 1, 'кашу': 1, 'спит': 1}
ест 1
кашу 1
кот 3
спит 1
Главная строка — freq[word] = freq.get(word, 0) + 1. Если слово встретилось впервые, get вернёт 0, и счётчик станет равен 1; если уже было — к старому значению прибавится единица. Так считают буквы в тексте, голоса в голосовании, повторы чисел в данных. Без словаря пришлось бы заводить переменную на каждое возможное слово, что невозможно, если слова заранее неизвестны. Тот же приём лежит в основе подсчёта голосов, статистики бросков кубика и поиска повторов: меняется только то, что считается ключом. Документация Python предлагает для этого и готовый инструмент — Counter из модуля collections, но сначала полезно написать подсчёт руками.
Вложенные словари
Значением словаря может быть другой словарь или список — так получается вложенная структура, похожая на карточки с несколькими полями. К вложенным данным обращаются цепочкой квадратных скобок.
pets = {
"Барсик": {"вид": "кот", "возраст": 3},
"Шарик": {"вид": "пёс", "возраст": 5},
}
print(pets["Шарик"]["возраст"])
pets["Барсик"]["возраст"] += 1
for name, info in pets.items():
print(name, "—", info["вид"], info["возраст"])
Запись pets["Шарик"]["возраст"] читается слева направо: сначала берём карточку Шарика, затем в ней — поле «возраст». Глубже двух-трёх уровней такие структуры читать трудно; если полей становится много, программисты переходят к классам. Для школьных проектов вроде викторины, где у вопроса есть текст, варианты и правильный ответ, вложенный словарь подходит отлично.
Словарь или список: что выбрать
Выбор зависит от того, как вы будете искать данные: если по номеру позиции — нужен список, если по названию — словарь. В таблице 5 типичных ситуаций.
| Задача | Что выбрать | Почему |
| Оценки за четверть по порядку | Список | Важен порядок, ищем по номеру |
| Телефоны друзей по имени | Словарь | Ищем по имени, а не по номеру |
| Сколько раз встречается каждая буква | Словарь | Буква — ключ, количество — значение |
| Очередь игроков на ход | Список | Порядок и добавление в конец |
| Цены товаров в магазине | Словарь | Товар — ключ, цена — значение |
Хорошая проверка: если в программе появляются два параллельных списка, names и phones, где элемент с одним индексом относится к одному человеку, — это знак, что нужен словарь. Подробно про упорядоченные наборы — в уроке про списки в Python.
Задачи на словари Python с автопроверкой
Чтобы закрепить тему, решите 6 задач в тренажёре вверху страницы: напишите код, нажмите «✓ Проверить», и программа прогонит его на тестах. Ошибочный тест покажет «Ожидалось» и «Получилось».
Задачи построены по нарастающей: перевод через get, подсчёт букв, телефонная книга, самое частое слово, сумма очков команд и стоимость покупки. В нескольких задачах вывод требуется в алфавитном порядке — используйте sorted. Подсказку открывайте после пары попыток, решение — в последнюю очередь. Исследование Roediger и Karpicke показало: попытка вспомнить и проверить себя закрепляет знания лучше перечитывания[2]. Применить пары «ключ — значение» в настоящей программе поможет викторина на Python.
Когда словарь не подходит
Словарь не подходит, когда важен порядок позиций, нужны повторяющиеся ключи или данные — просто последовательность. В этих 4 случаях лучше другая структура.
- Нужен 3-й по счёту элемент. У пар нет номеров позиций, для этого существует список.
- У одного ученика 5 оценок по одному предмету. Ключ не может повторяться — храните под ключом список оценок.
- Нужно отсортировать 100 чисел. Это работа для списка и
sorted, ключи тут лишние. - Ключ — набор из 2 изменяемых значений. Список не может быть ключом; используйте кортеж или строку.
«В словаре можно хранить два одинаковых ключа с разными значениями». Это миф: второе присваивание по тому же ключу заменяет значение, а не добавляет пару.
«Порядок пар в словаре случайный». Это не подтверждается для Python 3.7 и новее: пары хранятся в порядке добавления, это закреплено в документации языка.
Чего делать нельзя
Нельзя обращаться к ключу через квадратные скобки, если не уверены, что он есть, — программа упадёт с KeyError. Остальные правила тоже защищают от неожиданных остановок.
- Нельзя использовать список в роли ключа — Python выдаст TypeError: unhashable type.
- Не следует добавлять или удалять ключи во время перебора того же набора пар — будет RuntimeError.
- Не стоит путать
"5"и5в ключах: это разные ключи, и поиск по числу не найдёт строку. - Не допускается рассчитывать на
inдля поиска значения — оператор проверяет только ключи. - Нельзя называть переменную
dict: это имя встроенного типа, после заменыdict()перестанет работать. - Не стоит строить вложенность глубже 3 уровней в учебной программе: такой код трудно читать и отлаживать.
Оформление имён и отступов описывает руководство PEP 8[3], его пересказ — на странице про стиль кода Python.
Источники
[1] The Python Tutorial (официальная документация Python 3). Python Software Foundation.
[2] Roediger H. L., Karpicke J. D. Test-enhanced learning: Taking memory tests improves long-term retention // Psychological Science. 2006. Vol. 17, № 3. P. 249–255.
[3] van Rossum G., Warsaw B., Coghlan N. PEP 8 — Style Guide for Python Code. Python Software Foundation, 2001.
Вопросы и ответы
Чем ключ отличается от значения?
Ключ — это то, по чему ищут, а значение — то, что находят. Ключи уникальны, значения могут повторяться.
Как узнать все ключи?
Метод keys() вернёт их набор, а list(d.keys()) или просто list(d) превратит его в список.
Можно ли сделать значением список?
Да, например {"Аня": [5, 4, 5]}. Добавить оценку можно так: marks["Аня"].append(3).
Как отсортировать пары по значению?
Используйте sorted(d.items(), key=lambda p: p[1]) — получится список пар, упорядоченных по второму элементу. Для обратного порядка добавьте reverse=True.
Что вернёт len у пустых фигурных скобок?
Ноль: в {} нет ни одной пары. Пустые фигурные скобки создают именно пустой набор пар, а не множество.
Где эта структура встречается в реальных программах?
Почти везде: настройки игры, переводы интерфейса, данные из интернета в формате JSON. Освоив пары «ключ — значение», легче понять, как устроены большие программы.