logo
Ещё

Как взять значение из словаря Python и не сломать приложение?

Словари ломают мозг новичкам в программировании. Проблема в основном кроется в индексации: если в привычных списках каждый элемент имеет свой порядковый номер, то в словарях этим номером является произвольное (за некоторыми ограничениями) значение. Словари используются в любых крупных приложениях, и вам нужно обязательно знать основные методы, которые Python предоставляет для словаря: как его быстро создать, как создается копия словаря, как устанавливать в него значения и как из их словаря брать. Ниже мы рассмотрим два последних вопроса – как создавать новые пары {ключ: значение} и как безопасно брать их, используя встроенную функцию dict.


Значение словарей для языка программирования Python

Перед тем как приступить непосредственно к главной теме статьи, имеет смысл немного подробнее описать актуальность умения работать со словарями для программистов, использующих Питон. Дело в том, что именно этот элемент нередко и совершенно справедливо называют базисом данного языка программирования. Даже утверждение, что Python попросту сформирован вокруг разнообразных словарей – модулей, классов, объектов и т.д. – если и является преувеличением, то очень небольшим.

В процессе программирования словари используются практически повсеместно и для решения самых разных задач. Для их индексирования используются ключи, каждый из которых имеет определенное значение. Именно такой подход к разработке ПО обеспечивает простоту и лаконичность программного кода, созданного с помощью Python. Стоит ли удивляться тому вниманию, которое уделяется изучению словарей в целом и конкретному поиску определенного ключа/значения в словаре. Причем задачу требуется решить таким образом, чтобы полученный результат не привел к неисправности или сбоям в работе программы.

Способ первый, плохой: прямое обращение

Для этого и двух последующих способов мы будем использовать тестовый код, который вы сами можете запустить и посмотреть на результат. Первые 3 строки кода – инициализация – всегда будут одинаковыми. Вот они:

list_of_customers = ["Anna", "Ivan", "Peter"]

discount_amount = 5

customers_dictionary = {list_of_customers[i]: discount_amount for i in range(len(list_of_customers))}

Если вы не понимаете, что здесь происходит – смотрите раздел «Немного о списочных выражениях». В первой строке мы создаем список клиентов, во второй кладем в переменную базовую скидку для этих клиентов (5%), в третьей создаем словарь с парами {клиент: размер_скидки}. Если мы вызовем

print(customers_dictionary)

то увидим следующее:

{'Anna': 5, 'Ivan': 5, 'Peter': 5}

Если нам нужно узнать, какая скидка у клиента, мы можем написать

print(customers_dictionary["Anna"])

и в консоли появится число 5 – значение ключа “Anna”. Только вот если мы обратимся к любому несуществующему ключу (например, случайно напишем “Annna”), то Python 3 прервет исполнение и выбросит исключение. Программа будет принудительно закрыта процессором, и это точно не то, чего вам хотелось бы. Эту проблему можно обойти через обработку исключений, но для серьезного программирования Python такой выход не подходит – сложно, ненадежно. Python предоставляет куда более удобные методы взятия (и установки при необходимости) значения: методы .get() и .setdefault().

Главным недостатком такого способа вызова значения из словаря посредством квадратных скобок становится высокая вероятность выдачи программой исключения в виде ошибки, например, KeyError. Оно выступает следствием отсутствия ключа в словаре, что бывает достаточно часто. Особенно актуальной проблема является для объемных и часто меняющихся данных, характерных для значительной части динамических систем.

Чтобы нивелировать такой существенный минус, используются два метода. Первый предусматривает задействование конструкции try/except, которая предназначена для замены сообщения об ошибке на что-то более приятное, например, «Такой ключ отсутствует». В этом случае программный код имеет следующий вид.

try:

author['age']

except KeyError:

print('Такого ключа нет')

>>> 'Такого ключа нет'

Второй метод предполагает использованием встроенной библиотеки под названием collections или, что будет правильнее, готовой структуры из нее в виде defaultdict. Она представляет собой расширение к стандартным словарям, которое способно давать значение по умолчанию в случае запроса несуществующего ключа.

Способ второй, хороший: методом get

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

some_dict.method_name(arguments)

Для того, чтобы использовать метод get() в примере выше, вам нужно написать:

print(customers_dictionary.get("Anna"))

Print можно и не писать, но тогда метод вернет значение в никуда, и оно просто исчезнет, а с помощью принта мы можем посмотреть результат. Приведенная выше строка кода выводит число «5»

А что будет, если мы с помощью этого метода обратимся к элементу, которого в словаре нет? Например, исполним следующий код:

print(customers_dictionary.get("Oleg"))

С этом случае .get() возвращает None. Причем он вернет None как отдельный объект – еще в ранних версиях Python для None и Void добавили отдельный тип объекта. По умолчанию None возвращается, когда искомый ключ не найден, но вы можете переопределить этот возврат, для чего у .get() есть второй, необязательный параметр. Если вы напишете:

print(customers_dictionary.get("Oleg", "Не найдено"))

то ответом будет строка «Не найдено», так как метод не смог найти указанный вами ключ. Если же вы укажете в качестве первого аргумента «Anna», а в качестве второго «Не найдено», то метод вернет число 5.

Метод .get() ничего не меняет в словаре – он только возвращает какое-то значение. Запомните, это будет важно дальше.

Способ третий, альтернативный: методом setdefault

Есть еще один метод, возвращающий значение из словаря – .setdefault(). Он имеет 2 обязательных аргумента: какой ключ искать и что записывать в словарь, есть ключ не найден. Результата у исполнения этого метода может быть 2:

  1. Ключ, указанный вами, найден. В этом случае .setdefault() просто возвращает значение ключа.
  2. Ключ, указанный вами, не найден. В этом случае .setdefault() создает новый ключ (первый аргумент функции) и присваивает ему указанное значение (второй аргумент функции).

Другими словами, метод .setdefault() обладает следующим и весьма обширным функционалом, который включает:

  • самостоятельное создание ключа;
  • создание еще одного значения, дополняющего ключ, заданное программистом (нередко используется опция по умолчанию, например, None);
  • добавление указанной пары в существующий словарь;
  • возврат значения, в зависимости от результатов поиска основного ключа.

Если мы исполним код:

print(customers_dictionary.setdefault("Anna", 10))

на экран будет выведено число «5», поскольку в словаре нашелся ключ «Anna» (и второй аргумент не был задействован). Если же мы исполним код:

print(customers_dictionary.setdefault("Oleg", 10))

то на экран будет выведено «10»: метод увидит, что ключа «Oleg» нет, после чего создаст его и в качестве значения укажет 10. Таким образом, .setdefault() обновляет словарь.

Зачем нужно иметь 2 разных метода? Потому что это удобно. Например, у вас есть словарь клиентов, и вам нужно проверить скидку одного конкретного клиента. Вы пишете запрос:

print(customers_dictionary.get("Annna", "Не найдено"))

Как видите, вы случайно допустили ошибку. Что будет, если вы используете .setdefault()? Создастся новый клиент Annna, и у этого клиента в скидке будет лежать строка «Не найдено». Это – очень плохо, потому что если у вас есть метод, перебирающий все ключи словаря и что-то делающий с числовыми значениями этих ключей, строка «Не найдено» вам все сломает.

А теперь предположим, что вы регистрируете нового клиента, которому нужно выдать скидку. Проблема в том, что клиент не помнит, регистрировался ли у вас раньше. Вы, конечно, можете методом .get() попытаться поискать его в базе, если клиент не будет найден – создать нового и назначить ему скидку. Но это – 3 строки кода. Зачем писать 3 строки кода, если можно уместить все в одну?

print(customers_dictionary.setdefault("Oleg", 5))

Если клиент будет найден, вам просто вернется величина его скидки – можете ни к чему ее не присваивать, и результат будет отброшен. А если клиента не было – он создастся со скидкой в 5%. И вам ничего не нужно проверять.

Немного о списочных выражениях

На всякий случай расскажем вам про пару фишек при работе со словарями, которые пригодятся вам в повседневной жизни. Одна из самых полезных – это списочные выражения, выше мы использовали один из них. У каждого iterable-объекта (того, элементы которого можно пересчитать) в Python есть списочное выражение (list comprehension). Если вас сбивает с точку слово «список» в названии, то поясняем – списочные выражения могут применяться не только к спискам, но списки эту фишку получили первыми, поэтому название и закрепилось. Создать словарь или другой перечисляемый объект в этом случае можно одной строкой кода, при этом можно тонко «настроить» его генерацию. Списочное выражение устроено следующим образом:

variable = {key: value for key in iterable if something}

где:

  • variable – переменная, к которой будет привязан словарь;
  • key – ключ;
  • value – значение;
  • iterable – любой перечисляемый объект;
  • something – какое-то условие.

Последняя часть, «if something», не обязательна, остальные должны присутствовать. Работает все следующим образом:

  1. Запускается цикл for, в котором перебираются все iterable.
  2. Значение текущей итерации записывается в переменную key.
  3. Если условие if something – верное, то в словарь записывается пара key: value.
  4. Повторяется, пока есть что перебирать в цикле.
  5. Готовый словарь присваивается переменной.

Это может показаться сложным, но списочные выражения позволяют создавать большие и сложные словари в одну строку. Например, у вас есть словарь all_customers, в котором ключами указаны имена клиентов, а значениями – процент скидки. Вам нужно на основе этого словаря создать новый, customers_goe_10, в котором будут храниться все клиенты, у которых 10% скидки или больше (goe = greater or equal). Если вы не хотите перебирать ключи в цикле, можно сделать так:

customers_goe_10 = {item[0]: item[1] for item in all_customers.items() if item[1] >= 10}

Что за магия только что произошла? Все просто: у любого словаря есть ряд методов items (методы keys, values и непосредственно items). Когда вы обращаетесь к .items(), то получаете все значения в виде кортежа, то есть {“Anna”: 5} превращается в (“Anna”, 5). Каждая такая пара записывается в переменную item, значения можно получать по индексам: item[0] соответствует ключу (“Anna”), item[1] соответствует значению (5). Затем в новом словаре создается пара ключ: значение (item[0]: item[1]) при условии, что значение больше или равно 10 (if item[1] >= 10). Это повторяется для каждой пары из all_customers

Что почитать

Курс «Python-разработчик» от Skillbox

Школа

Skillbox

Стоимость

104 551 руб

Цена в рассрочку

3 373 руб/мес

Длительность курса

10 месяцев

Программа трудоустройства

Есть

Формат

Запись лекций, Онлайн занятия с преподавателем

Курс «IT-специалист с нуля» от Skillfactory

Школа

Skillfactory

Стоимость

164 873 руб

Цена в рассрочку

4 580 руб/мес

Длительность курса

18 месяцев

Программа трудоустройства

Есть

Формат

Запись лекций, Онлайн занятия с преподавателем

Курс «Python-разработчик» от Академия «Синергия»

Школа

Академия «Синергия»

Стоимость

87 200 руб

Цена в рассрочку

3 633 руб/мес

Длительность курса

6 месяцев

Программа трудоустройства

Есть

Формат

Запись лекций, Онлайн занятия с преподавателем

Вывод

Вкратце:

  • Узнать значение по ключу в словаре Python 3 можно через прямое обращение, через метод .get() и через метод .setdefault().
  • Получение значение через прямое обращение к ключу – очень опасный метод, потому что если ключа в словаре не обнаружится, то программа выкинет исключение, и процессор принудительно завершит программу.
  • Метод .get() возвращает значение ключа и имеет 2 аргумента: ключ (обязательно) и дефолтное значение (необязательно). Если ключ есть – вернется значение. Если ключа нет – вернется дефолтное значение (если указано) или объект None. .get() не меняет словарь.
  • Метод .setdefault() возвращает значение ключа, если нашел его в словаре, и создает новый ключ + устанавливает ему указанное значение, если ключа не нашлось. Имеет 2 обязательных аргумента – ключ и значение. Меняет словарь.
  • Если вам нужно просто проверить наличие ключа и получить значение – используйте .get(). Если вам нужно получить значение в любом случае, даже если для этого нужно создать ключ – используйте .setdefault().
Часто ищут