Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 1 из 20 Начальный 15 мин 100 XP

Словарь в Python: ключ и значение

Словарь — структура «ключ — значение»: достаём номер телефона по имени из телефонной книги, сравниваем dict со списком на живых данных и заглядываем под капот — как Python находит значение по ключу.

Редакция Питоники

До сих пор данные жили в списках: элементы лежат по порядку, и достают их по номеру. Но у многих вещей из жизни номера нет — есть имя. Номер телефона ищут по имени человека, цену товара — по названию, перевод слова — по самому слову. Для таких пар «имя — данные» в Python есть словарь: dict. В этом первом уроке раздела создаём телефонную книгу и учимся доставать из неё значения.

Первый словарь: телефонная книга

Словарь — это набор пар: ключ и значение. Ключ — то, по чему ищем; значение — то, что находим. В телефонной книге ключ — имя, значение — номер. Пары перечисляются в фигурных скобках через запятую, ключ отделяется от значения двоеточием:

телефонная книга
phone = {
    'Анна': '+7 900 111-22-33',
    'Борис': '+7 900 222-33-44',
    'Вера': '+7 900 333-44-55',
}

print(phone)
print(phone['Борис'])
Вывод
{'Анна': '+7 900 111-22-33', 'Борис': '+7 900 222-33-44', 'Вера': '+7 900 333-44-55'}
+7 900 222-33-44

Разбираем. phone — словарь из трёх пар. Запись phone['Борис'] читается «значение из phone под ключом Борис»: в квадратных скобках стоит ключ, а не номер. Кавычки нужны, потому что ключ здесь — строка, имя человека. Найденное значение можно сразу использовать — печатать, складывать, сравнивать (а что будет, если ключа нет — отдельная история, ей посвящён урок про KeyError).

значение работает как обычное число
prices = {'молоко': 89, 'хлеб': 42, 'сыр': 590}

total = prices['молоко'] + prices['хлеб']
print(total)
print(prices['сыр'] > 500)
Вывод
131
True

dict против списка: имя против номера

номер против имени
goods_list = ['молоко', 'хлеб', 'сыр']
print(goods_list[0])

goods = {'молоко': 89, 'хлеб': 42, 'сыр': 590}
print(goods['сыр'])
Вывод
молоко
590

Список и словарь решают одну задачу — хранят набор значений, — но ищут по-разному: список отвечает на вопрос «что лежит на позиции 0?», словарь — «сколько стоит сыр?». У списка goods_list нулевая позиция занята молоком — но стоит переставить товары, и goods_list[0] вернёт уже другое. Словарь от порядка не зависит: ключ 'сыр' — это имя, а не позиция. Ключ — это имя, по которому лежит значение, а значение — то, что под этим именем хранится.

СписокСловарь
Элемент достаётся по номеру: goods_list[0]Элемент достаётся по имени: goods['сыр']
Номер — позиция, её надо помнить и беречь от перестановокКлюч — смысл: имя, название, id — его легко вспомнить
Годится для упорядоченного: шаги, дни, очередьГодится для справочников: цены, настройки, контакты
  • настройки программы: {'theme': 'dark', 'lang': 'ru'} — ключ задаёт, что настраиваем, значение — как;
  • данные из внешнего мира: JSON-ответы API по сути и есть словари — об этом раздел про json;
  • подсчёты: ключ — слово, значение — сколько раз оно встретилось.

Ключ и значение: несимметричная пара

Слово «пара» обманчиво: ключ и значение ведут себя совсем по-разному. Ключ уникален — двух пар с одинаковым ключом в словаре не бывает. Значение, наоборот, повторяться может сколько угодно раз: два товара могут стоить одинаково, и это ничего не ломает. Ещё одно различие: ключ обязан оставаться неизменным всё время жизни словаря, а значение можно менять хоть каждую минуту. Именно поэтому словарь отлично работает там, где список уже не справляется: номер телефона у человека может совпасть с чьим-то ещё, но один человек не может занимать два места в телефонной книге.

одинаковые значения — это нормально
stock = {
    'молоко': 12,
    'сыр': 12,
    'хлеб': 5,
}

print(stock['молоко'])
print(stock['сыр'])
print(len(stock))
Вывод
12
12
3

Значения совпадают, а пара всё равно одна на каждого товара — длина словаря равна трём. Ещё один нюанс, на котором спотыкаются даже опытные: ключи 1 и True для Python — один и тот же ключ, потому что True равно 1. Проверим.

True и 1 — один ключ
d = {True: 'да', 1: 'одна'}

print(d)
print(len(d))
print(d[True])
Вывод
{True: 'одна'}
1
одна

В словаре осталась одна пара: при добавлении 1 значение 'да' было перезаписано на 'одна'. Никакой ошибки, никакого предупреждения. Про такие совпадения подробнее — в уроке про ключи.

Под капотом: как Python находит значение по ключу

Словарь устроен как хеш-таблица. Для каждого ключа Python вычисляет число — хеш, — и по остатку от деления на размер внутренней таблицы решает, в какую «ячейку» положить пару. При поиске по ключу хеш считается заново, и Python идёт сразу в нужную ячейку, а не перебирает все пары подряд. Отсюда то самое ощущение мгновенности: на словаре из трёх пар и на словаре из трёх миллионов поиск занимает примерно одинаковое время. Плата за это — память: словарь хранит ещё и хеши, и служебные записи о занятости ячеек, поэтому на одинаковых данных он заметно тяжелее списка.

хеш одного и того же ключа
print(hash('молоко') == hash('молоко'))
print(hash(1) == hash(1.0))
print(hash('молоко') == hash('хлеб'))
Вывод
True
True
False

Один и тот же ключ всегда даёт один и тот же хеш — на этом и держится вся адресация. Но хеши разных ключей вполне могут совпасть: hash('молоко') и hash('хлеб') в примере разные, а для других строк бывает и одинаково. Словарь в таком случае не сломается, а просто разложит обе пары по соседним ячейкам и при поиске проверит их лишний раз. Именно поэтому говорят, что словарь даёт «почти» постоянное время, а не абсолютно постоянное.

список в ключе
stock = {}
stock[[1, 2]] = 'список в ключе'
Вывод
Traceback (most recent call last):
  File "example.py", line 2, in <module>
    stock[[1, 2]] = 'список в ключе'
    ~~~~~^^^^^^^^
TypeError: unhashable type: 'list'
Блок не запускается: код специально падает. Указатель в traceback показывает, на каком именно месте стоит список — на месте ключа.

Сценарий: прайс, который меняется каждый день

Возьмём задачу, где список ломается буквально на второй день работы. Есть прайс: хлеб — 42, молоко — 89, сыр — 590. Списком это выглядит так: ['хлеб', 'молоко', 'сыр'] и отдельно [42, 89, 590]. В первый день работает. На второй приходит заказ «молоко, две штуки», и программист дописывает новый товар в конец — потому что дописать в середину списка значит сдвинуть все последующие индексы. Через неделю в прайсе двадцать позиций, цена молока поехала, а счёт выставляется не на ту сумму. Найти, где ошибка, невозможно: индексы-то правильные, просто относятся к другим товарам.

Со словарём эта ошибка невозможна в принципе. Цена лежит рядом с названием, и названия не меняют свою цену при добавлении нового товара: prices['молоко'] всегда вернёт 89, пока это значение не перезапишут явно. Мы в редакции так и делали — перевели прайс на словарь и больше к индексам не возвращались. Мой практический совет: если в структуре данных есть хотя бы одно «имя» — берите словарь, даже если элементов двадцать. А если это просто очередь шагов или дни недели — оставляйте список, там позиция и правда важна.

прайс на словаре
prices = {'хлеб': 42, 'молоко': 89, 'сыр': 590}

total = prices['хлеб'] + prices['молоко']
print(total)

prices['молоко'] = 95
print(prices['молоко'])
print(len(prices))
Вывод
131
95
3

Что дальше

Первый словарь собран и прочитан. Дальше — как создавать словари разными способами и какие значения вообще можно поставить в ключ: в уроке 2 разбираем литерал, функцию dict() и пустой словарь под будущие пары. А если хочется сразу понять, что будет, когда ключа в словаре не окажется, — в уроке 3 разбираем KeyError и то, как его читать.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

book = {'кот': 'мяу', 'гусь': 'га'}

print(book['гусь'])
prices = {'чай': 50, 'кофе': 120}

print(prices['кофе'] - prices['чай'])
d = {'a': 1, 'b': 2}

print(d['b'])
print(d['a'] + d['b'])
Проверь себя
0 / 8

1. Что такое словарь в Python?

2. В словаре phone = {'Анна': '111'} — как достать номер Анны?

3. Чем словарь принципиально отличается от списка?

4. Что напечатает print(phone) для словаря из трёх пар?

5. Почему запись phone[Анна] без кавычек — ошибка?

6. Что произойдёт, если попробовать использовать список как ключ словаря?

7. Почему поиск по ключу в словаре работает быстро даже при миллионе пар?

8. Словарь d = {True: 'да', 1: 'одна'}. Сколько пар в нём и что под ключом True?

Карточки терминов
Запомнено: 0 / 9
Практика

Собери словарь prices с тремя парами: молоко — 89, хлеб — 42, сыр — 590. Выведи словарь целиком, затем достань цену сыра по ключу и выведи её.

practice.py
Вопросы и ответы по уроку

Что такое словарь в Python простыми словами?

Это контейнер, который хранит пары «ключ — значение» и находит значение по ключу. Как телефонная книга: имя — ключ, номер — значение. Записывается фигурными скобками, а значение достаётся записью вида phone['Анна'].

Урок 2: создание словаря

Чем словарь отличается от списка?

Список ищет по номеру позиции: goods[0] — «что лежит первым». Словарь ищет по имени: goods['сыр'] — «сколько стоит сыр». Поэтому при перестановке элементов список даёт уже другие данные по старым индексам, а словарь находит значение по ключу независимо от порядка пар.

Как получить значение из словаря по ключу?

Записать ключ в квадратных скобках: phone['Борис']. Ключ-строка — в кавычках, число — без. Если ключа в словаре нет, такая запись упадёт с KeyError — как читать без падений, разобрано в уроке про метод get.

Урок 4: метод get

Может ли ключ быть числом?

Да: словарь {1: 'январь', 2: 'февраль'} корректен, доступ — по числу без кавычек: months[1]. Ключом годится любое неизменяемое значение; чаще всего берут строки и числа. Полный разбор — в уроке про создание словарей.

Почему список нельзя использовать как ключ словаря?

Словарь находит значение по хешу ключа, а хеш считается заранее и хранится внутри неизменяемого значения. Список изменяемый — его хеш изменился бы, и словарь перестал бы находить пару. Python запрещает такое на уровне типа: TypeError: unhashable type: 'list'. Годятся строки, числа и кортежи из неизменяемых элементов.

Урок 18: ключи — что можно и что нельзя

Чем словарь лучше списка на практике?

Словарь привязывает значение к имени, а не к позиции. Добавление нового товара в середину списка сдвигает все последующие индексы, и цена одного товара молча начинает соответствовать другому. Со словарём такого не происходит: prices['молоко'] вернёт своё значение независимо от того, сколько позиций добавилось. Если в данных есть хоть одно имя — берите словарь.

Что произойдёт, если в словаре встретятся True и 1?

Для Python это один и тот же ключ: True равно 1, поэтому вторая пара перезапишет первую, а длина словаря будет 1. Ни ошибки, ни предупреждения. Такие совпадения редки в прикладном коде, но в задачах с флагами и счётчиками попадаются — проверяйте, не смешиваете ли вы типы ключей.

Понравился урок? Сошлитесь на него

«Ключ — это имя, по которому лежит значение, а значение — то, что под этим именем хранится.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.