В списке к элементу обращаются по порядковому месту: первый, второй, десятый. Это удобно, пока порядок для вас что-то значит. Но если вам нужно хранить не последовательность, а связь двух вещей — имя и номер телефона, слово и его перевод, товар и цену — считать элементы по номерам становится неудобно и ненадёжно: порядок может сбиться, а нужное значение всё равно нужно искать перебором.
Для таких связей в Python есть отдельная коллекция, которая ищет значение не по месту, а по имени, которое вы сами назначаете. Именно её разберём в этом уроке: как создавать, читать, менять и удалять данные внутри неё, а заодно — какие типичные ошибки с ней случаются у новичков.
Что такое словарь
Простыми словами: представьте бумажную телефонную книгу. В ней вы не листаете страницы подряд в поисках нужного номера — вы сразу открываете книгу на букве нужного имени. Имя — это то, по чему вы ищете, номер телефона — то, что вы в итоге получаете. Ровно так же устроен словарь в Python: у каждой записи есть имя-опознаватель и то, что под этим именем хранится.
Официально это называется словарь (англ. dict, от dictionary) — коллекция, в которой данные хранятся парами ключ: значение. По ключу вы почти мгновенно получаете значение, без перебора всех записей подряд, как в списке.
contacts = {"Мама": "+7-900-111-22-33", "Антон": "+7-900-444-55-66"}
print(contacts["Мама"])
contacts = {...}— словарь из двух пар: слева от двоеточия — ключ (имя), справа — значение (номер).contacts["Мама"]— обращение к значению по ключу в квадратных скобках: «дай мне то, что лежит под ключом "Мама"».print(...)— печатает результат: строку с номером телефона.
+7-900-111-22-33
Создание словаря: {} и dict()
Простыми словами: чистый блокнот для будущих записей можно завести пустым или сразу вписать в него первые пары — способ записи от этого не меняется, меняется только момент, когда данные появляются.
Официально словарь можно создать двумя способами. Первый — фигурные скобки {}, самый частый в реальном коде. Второй — готовая функция dict(), где ключи пишут как имена аргументов без кавычек.
empty = {}
student = {"name": "Алина", "age": 19}
same_student = dict(name="Алина", age=19)
print(student)
print(same_student)
empty = {}— пустые фигурные скобки создают пустой словарь, без единой пары.student = {"name": "Алина", "age": 19}— две пары через запятую: ключ"name"хранит"Алина", ключ"age"хранит19.dict(name="Алина", age=19)— та же самая пара данных, но ключи пишутся как параметры функции, поэтому без кавычек и без двоеточий — только со знаком равенства.print(student)иprint(same_student)— печатают оба словаря, и результат у них одинаковый.
{'name': 'Алина', 'age': 19}
{'name': 'Алина', 'age': 19}
Ключи и значения: правила
Простыми словами: в телефонной книге не бывает двух разных абонентов, записанных под совершенно одинаковым именем-опознавателем — если вы записываете новый номер под уже существующим именем, старый номер просто стирается и заменяется новым. Со словарём в Python то же самое: ключ всегда один и уникален, а вот значений с одинаковым содержимым может быть сколько угодно.
Официально, если в словаре повторить один и тот же ключ дважды, второе значение перезапишет первое — никакой ошибки при этом не будет. А ключом может быть только неизменяемый тип данных: строка, число или кортеж. Список ключом быть не может, потому что список можно изменить после создания, а Python требует, чтобы ключ оставался постоянным.
d = {"color": "red", "color": "blue"}
print(d)
scores = {"Алина": 95, "Борис": 95}
print(scores)
d = {"color": "red", "color": "blue"}— ключ"color"указан дважды: второе значение переписывает первое ещё на этапе создания словаря.print(d)— в результате в словаре останется только одна пара, с последним указанным значением.scores = {"Алина": 95, "Борис": 95}— а вот значение95повторяется у двух разных ключей — и это совершенно нормально, ограничение касается только ключей.
{'color': 'blue'}
{'Алина': 95, 'Борис': 95}
TypeError прямо в момент создания такого словаря.Доступ к значению: [] и .get()
Простыми словами: если вы ищете в телефонной книге контакт, которого там нет, есть два поведения на выбор — расстроиться и захлопнуть книгу, либо спокойно сказать себе «такого нет» и жить дальше. В Python эти два поведения соответствуют двум разным способам чтения значения по ключу.
Официально квадратные скобки d[ключ] при отсутствующем ключе останавливают программу ошибкой KeyError. Метод .get(ключ) в этой же ситуации не падает, а тихо возвращает None — или значение по умолчанию, если вы передадите его вторым аргументом.
person = {"name": "Алина", "age": 19}
print(person["name"])
print(person.get("city"))
print(person.get("city", "не указан"))
person["name"]— ключ есть, значение возвращается без проблем.person.get("city")— ключа"city"в словаре нет, программа не падает, а метод возвращаетNone.person.get("city", "не указан")— второй аргумент.get()— значение по умолчанию, которое вернётся вместоNone, если ключа нет.
Алина
None
не указан
[], код будет короче. Если не уверены — всегда .get(): он не остановит программу неожиданной ошибкой.Изменение и добавление пар
Простыми словами: чтобы вписать в телефонную книгу новый номер, вы просто пишете имя и номер на подходящей строке — неважно, была там уже какая-то запись под этим именем или страница была пустая. Python поступает так же: одна и та же команда и добавляет новую пару, и меняет существующую.
Официально присваивание вида d[ключ] = значение работает по правилу: если ключ уже есть в словаре — его значение заменяется на новое, если ключа нет — создаётся новая пара.
profile = {"name": "Алина", "age": 19}
profile["age"] = 20
profile["city"] = "Москва"
print(profile)
profile["age"] = 20— ключ"age"уже существует, поэтому его старое значение19заменяется на20.profile["city"] = "Москва"— такого ключа раньше не было, поэтому в словарь добавляется новая пара.print(profile)— в результате словарь содержит и изменённую запись, и новую.
{'name': 'Алина', 'age': 20, 'city': 'Москва'}
Удаление пар: del и .pop()
Простыми словами: одну и ту же запись из блокнота можно просто зачеркнуть и забыть, а можно сначала переписать себе номер на отдельный листочек и только потом зачеркнуть — выбор зависит от того, нужно ли вам ещё само значение.
Официально команда del d[ключ] удаляет пару и ничего не возвращает. Метод .pop(ключ) удаляет пару и возвращает удалённое значение, поэтому его можно сразу сохранить в переменную. Со вторым аргументом — .pop(ключ, по_умолчанию) — метод не упадёт с ошибкой, если ключа не было.
data = {"a": 1, "b": 2, "c": 3}
del data["a"]
print(data)
removed = data.pop("b")
print(removed)
print(data)
result = data.pop("z", "не найдено")
print(result)
del data["a"]— удаляет пару с ключом"a", ничего не возвращает.removed = data.pop("b")— удаляет пару с ключом"b"и одновременно кладёт её значение,2, в переменнуюremoved.data.pop("z", "не найдено")— ключа"z"в словаре нет, поэтому метод возвращает значение по умолчанию вместо ошибки.
{'b': 2, 'c': 3}
2
{'c': 3}
не найдено
.pop(). Если хотите просто убрать запись и она вам больше не нужна — del короче.Проверка наличия ключа: in
Простыми словами: прежде чем лезть в книгу за номером, иногда полезно сначала быстро проверить — а есть ли вообще такая запись, чтобы не наткнуться на ошибку.
Официально оператор in, который вы уже использовали со строками и списками, для словаря проверяет наличие именно ключа, а не значения. Это самый частый способ подстраховаться перед обращением по [].
config = {"debug": True, "port": 8080}
print("port" in config)
print("timeout" in config)
if "debug" in config:
print("Режим отладки настроен")
"port" in config— ключ"port"есть в словаре, результатTrue."timeout" in config— такого ключа нет, результатFalse.if "debug" in config:— типичный приём: сначала убеждаемся, что ключ есть, и только потом обращаемся к нему внутри блокаif.
True
False
Режим отладки настроен
Когда разбирали перебор коллекций, вы уже перебирали словарь циклом for и доставали пары через .items() — та же самая формула, for ключ, значение in словарь.items():, работает и здесь, просто теперь вы понимаете природу самого словаря глубже.
Вложенные словари
Простыми словами: анкета человека редко состоит из одних только простых полей — у адреса, например, есть свой город, улица и номер дома. Удобно хранить такой «адрес» не отдельными несвязанными ключами, а отдельным словарём внутри анкеты.
Официально значением словаря может быть любой тип данных — в том числе другой словарь или список. Так получаются вложенные структуры, а обращение к глубоко лежащему значению делается цепочкой квадратных скобок подряд.
user = {
"name": "Алина",
"address": {"city": "Москва", "street": "Ленина"},
"skills": ["Python", "SQL"]
}
print(user["address"]["city"])
print(user["skills"][0])
user["address"]["city"] = "Казань"
print(user["address"]["city"])
"address": {"city": ..., "street": ...}— значением ключа"address"стал ещё один словарь.user["address"]["city"]— сначалаuser["address"]достаёт вложенный словарь, затем["city"]достаёт значение уже из него.user["skills"][0]— похожая цепочка, только второй шаг — это индекс списка, а не ключ словаря.user["address"]["city"] = "Казань"— меняет значение внутри вложенного словаря, сам внешний словарьuserпри этом не пересоздаётся.
Москва
Python
Казань
Объединение словарей: .update() и |
Простыми словами: если у вас есть общие настройки по умолчанию и личные настройки пользователя, итоговый результат — это общие настройки, поверх которых наложены личные, а где они совпадают — побеждают личные.
Официально метод .update() добавляет в словарь пары из другого словаря прямо в него же, изменяя исходный. Оператор | делает то же самое, но создаёт при этом совершенно новый словарь, не трогая оба исходных.
defaults = {"theme": "light", "timeout": 30}
user_settings = {"theme": "dark"}
config = defaults | user_settings
print(config)
print(defaults)
defaults | user_settings— создаёт новый словарь: берёт все пары изdefaults, а совпадающие ключи заменяет значениями изuser_settings.print(config)— печатает итоговый, объединённый словарь.print(defaults)— исходный словарьdefaultsпри этом не изменился, потому что|строит отдельный, новый результат.
{'theme': 'dark', 'timeout': 30}
{'theme': 'light', 'timeout': 30}
Частые ошибки
Обращение по [] к ключу, которого может не быть. Новички часто пишут d[ключ] там, где ключ появляется не всегда, и программа падает в неожиданный момент.
user = {"name": "Алина"}
print(user["age"]) # KeyError: 'age'
"age" в словаре нет, и квадратные скобки останавливают программу ошибкой KeyError. Если заранее неизвестно, есть ли ключ, используйте .get() с подходящим значением по умолчанию.Использование списка в качестве ключа. Список можно менять после создания, поэтому Python не разрешает делать его ключом.
bad = {[1, 2]: "координаты"} # TypeError: unhashable type: 'list'
(1, 2).Что важно запомнить
.get(ключ, по_умолчанию).del удаляет молча, .pop() — удаляет и возвращает значение..update() (меняет на месте) или | (создаёт новый).Проверьте себя
5 вопросов
Словарь оценок студентов
PremiumПодсчёт символов
PremiumТелефонная книга: найти абонента
Дан словарь phonebook с номерами телефонов. Найдите номер Антона и запишите его в переменную anton_phone. Затем найдите номер Виктора — такого контакта в книге нет — и, если его нет, запишите в переменную viktor_phone строку "нет в книге". Используйте метод .get(), чтобы поиск отсутствующего контакта не приводил к ошибке.