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

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

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

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

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

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

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

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

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)

Строки очень похожи, но если во втором случае новый список создаётся только тогда, когда ключ не будет найден, то в первой строчке объект пустого списка будет создаваться каждый раз. Конкретно затратами на создание пустого списка можно пренебречь. Однако, если вдруг затраты на создание значения по умолчанию окажутся велики, скажем, каждое создание потребует хождения в базу данных, то вариант с defaultdict сразу же окажется гораздо более предпочтителен!

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

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

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

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


<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

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

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

Для полного доступа к курсу нужна профессиональная подписка

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

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

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

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Отправляя форму, вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».

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

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

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

Отправляя форму, вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».