Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Инициализация новых значений и defaultdicts Python: Словари и Множества

Инициализация новых значений

Представьте ситуацию: вам нужно хранить в словаре в качестве значений что-нибудь изменяемое, скажем, списки. И вот в процессе работы с таким словарём вы попадаете в ситуацию, когда у вас есть ключ и элемент для добавления в список-значение, но — вот ведь незадача — ключ пока в словаре не представлен. Приходится писать подобный код:

if key not in dictionary:
    dictionary[key] = []  # инициализируем список
dictionary[key].append(value)  # изменяем список

Подобная ситуация встречается не так уж и редко. Это понимали и авторы стандартной библиотеки Python и дали словарю метод setdefault. Вышеупомянутый код можно переписать с использованием этого метода:

dictionary.setdefault(key, []).append(value)

Компактно и лаконично! Но что же делает метод setdefault? Метод принимает ключ и значение по умолчанию и возвращает ссылку на значение в словаре, связанное с указанным ключом. А если ключ в словаре отсутствует, то метод помещает по ключу то самое значение по умолчанию и возвращает ссылку на него!

В примере выше значением по умолчанию выступает пустой список [].

defaultdict

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

from collections import defaultdict
d = defaultdict(int)
d['a'] += 5
d['b'] = d['c'] + 10
d  # defaultdict(<class 'int'>, {'a': 5, 'c': 0, 'b': 10})

При создании словаря я указал в качестве аргумента функцию int. Если эту функцию вызвать без аргументов, то она вернёт 0 и именно этот вызов внутри словаря d и происходит всякий раз, когда нужно получить значение для несуществующего ключа. Поэтому d['a'] += 5 даёт в итоге 5, т.к. сначала для ключа 'a' создаётся начальное значение (делается вызов int() и получается 0), а потом к нему прибавляется 5. В строчке d['b'] = d['c'] + 10 создаются значения для ключей 'b' и 'c' и затем уже по ключу 'b' записывается сумма 0 + 10.

Вот ещё один пример — на этот раз с самодельной функцией-инициализатором:

def new_value():
    return 'foo'
x = defaultdict(new_value)
x[1]  # 'foo'
x['bar']  # 'foo'
x  # defaultdict(<function new_value at 0x7f2232cf5a60>, {1: 'foo', 'bar': 'foo'})

Если отбросить немного непонятное упоминание функции-инициализатора, видно, что по всем ключам, по которым я обращался к содержимому словаря, теперь записаны строки 'foo'.

Отличия defaultdict от обычного словаря c setdefault

Зачем же иметь оба способа, если они настолько похожи, спросите вы. Но давайте сравним эти две строки:

a.setdefault(key, []).append
# vs
b[key].append

# b это defaultdict(list)

Строки очень похожи, но если во втором случае новый список создаётся только тогда, когда ключ не будет найден, то в первой строчке объект пустого списка будет создаваться каждый раз. Так как значения аргументов всегда вычисляются до того как будет вызвана функция, в данном случае setdefault(key, []), конкретно затратами на создание пустого списка можно пренебречь. Однако, если вдруг затраты на создание значения по умолчанию окажутся велики, скажем, каждое создание потребует хождения в базу данных, то вариант с defaultdict сразу же окажется гораздо более предпочтителен!

Зачем же вообще использовать setdefault? Например для того, чтобы по разным ключам инициализировать разные значения! Т.к. значение по умолчанию передаётся каждый раз, мы можем по разным ключам хранить даже разные типы данных. С defaultdict у нас нет контроля над тем, какие значения по каким ключам класть: функция-инициализатор вызывается каждый раз одна и та же и ключ в неё не передаётся.

Наконец всегда остаются редкие случаи, когда и defaultdict не подходит, потому что нужно инициализировать значения по-разному, но не подходит и setdefault — новые значения неизменяемы и их не получится изменить по возвращаемой ссылке. Пример такого случая (вместе с решением задачи ненахождения ключа):

x['count'] = x.get('count', 0) + 1
x['path'] = x.get('path', '') + '/' + dir

Да, здесь присутствуют лишние хождения по одному и тому же ключу, но сам код читается неплохо и в данной ситуации, можно сказать, оптимален!


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

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

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 130 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
1 июня 10 месяцев

Используйте Хекслет по максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»