Использование типа Python defaultdict для обработки отсутствующих ключей

Оглавление

Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Посмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Обработка пропущенных ключей с помощью типа Python defaultdict

Распространенной проблемой, с которой вы можете столкнуться при работе со словарями Python , является попытка получить доступ к ключам, которых нет в словаре, или изменить их. Это вызовет ошибку KeyError и прервет выполнение вашего кода. Чтобы справиться с подобными ситуациями, стандартная библиотека предоставляет Python defaultdict type, класс, похожий на словарь, который доступен для вас в collections.

Тип Python defaultdict ведет себя почти так же, как обычный словарь Python, но если вы попытаетесь получить доступ к отсутствующему ключу или изменить его, то defaultdict автоматически создаст ключ и сгенерирует для него значение по умолчанию. Это делает defaultdict ценным средством для обработки отсутствующих ключей в словарях.

В этом уроке вы узнаете:

  • Как использовать тип Python defaultdict для обработки отсутствующих ключей в словаре
  • Когда и зачем использовать Python defaultdict вместо обычного dict
  • Как использовать defaultdict для группировки, подсчитывая и накапливая операции

Имея за плечами эти знания, вы будете в лучшей форме для эффективного использования языка Python defaultdict при решении повседневных задач программирования.

Чтобы извлечь максимальную пользу из этого руководства, вы должны иметь некоторое представление о том, что такое словари Python и как с ними работать. Если вам нужно освежиться, то ознакомьтесь со следующими ресурсами:

Бесплатный бонус: Нажмите здесь, чтобы получить шпаргалку по Python и изучить основы Python 3, такие как работа с типами данных, словари, списки и функции Python.

Обработка пропущенных ключей в словарях

Распространенной проблемой, с которой вы можете столкнуться при работе со словарями Python, является как обрабатывать отсутствующие ключи. Если ваш код в значительной степени основан на словарях или если вы постоянно создаете словари "на лету", то вскоре вы заметите, что работа с частыми KeyError исключениями может сильно раздражать и добавлять дополнительную сложность вашему коду. Со словарями Python у вас есть как минимум четыре доступных способа обработки отсутствующих ключей:

  1. Использовать .setdefault()
  2. Использовать .get()
  3. Используйте идиому key in dict
  4. Используйте блок try и except

В документах по Python объясняется .setdefault() и .get() следующим образом:

setdefault(key[, default])

Если key есть в словаре, верните его значение. Если нет, вставьте key со значением default и верните default. default значение по умолчанию равно None.

get(key[, default])

Возвращает значение для key, если key есть в словаре, в противном случае default. Если default не задано, то по умолчанию используется значение None, так что этот метод никогда не вызывает KeyError.

( Источник)

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

>>> a_dict = {}
>>> a_dict['missing_key']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    a_dict['missing_key']
KeyError: 'missing_key'
>>> a_dict.setdefault('missing_key', 'default value')
'default value'
>>> a_dict['missing_key']
'default value'
>>> a_dict.setdefault('missing_key', 'another default value')
'default value'
>>> a_dict
{'missing_key': 'default value'}


В приведенном выше коде вы используете .setdefault() для создания значения по умолчанию для missing_key. Обратите внимание, что в вашем словаре a_dict теперь есть новый ключ с именем missing_key, значение которого равно 'default value'. Этот ключ не существовал до того, как вы вызвали .setdefault(). Наконец, если вы вызовете .setdefault() для существующего ключа, то этот вызов никак не повлияет на словарь. Ваш ключ будет содержать исходное значение вместо нового значения по умолчанию.

Примечание: В приведенном выше примере кода вы получаете исключение, и Python показывает вам сообщение обратной трассировки, в котором говорится, что вы пытаетесь получить доступ к отсутствующему ключу в a_dict. Если вы хотите глубже разобраться в том, как расшифровывать и понимать обратную трассировку Python, ознакомьтесь с разделами Понимание обратной трассировки Python и Как получить максимальную отдачу от обратной трассировки Python.

С другой стороны, если вы используете .get(), то вы можете закодировать что-то вроде этого:

>>> a_dict = {}
>>> a_dict.get('missing_key', 'default value')
'default value'
>>> a_dict
{}


Здесь вы используете .get() для создания значения по умолчанию для missing_key, но на этот раз ваш словарь остается пустым. Это происходит потому, что .get() возвращает значение по умолчанию, но это значение не добавляется в базовый словарь. Например, если у вас есть словарь под названием D, то вы можете предположить, что .get() работает примерно так:

D.get(key, default) -> D[key] if key in D, else default


С помощью этого псевдокода вы можете понять, как работает .get(). Если ключ существует, то .get() возвращает значение, сопоставленное с этим ключом. В противном случае возвращается значение по умолчанию. Ваш код никогда не создает и не присваивает значение key. В этом примере значение default по умолчанию равно None.

Вы также можете использовать условные операторы для обработки отсутствующих ключей в словарях. Взгляните на следующий пример, в котором используется идиома key in dict:

>>> a_dict = {}
>>> if 'key' in a_dict:
...     # Do something with 'key'...
...     a_dict['key']
... else:
...     a_dict['key'] = 'default value'
...
>>> a_dict
{'key': 'default value'}


В этом коде вы используете оператор if вместе с оператором in, чтобы проверить, присутствует ли key в a_dict. Если это так, то вы можете выполнить любое действие с key или с его значением. В противном случае вы создаете новый ключ key и присваиваете ему значение 'default value'. Обратите внимание, что приведенный выше код работает аналогично .setdefault(), но занимает четыре строки кода, в то время как .setdefault() занимает только одну строку (в дополнение к тому, что он более удобочитаем).

Вы также можете обойти KeyError, используя блоки try и except для обработки исключения. Рассмотрим следующий фрагмент кода:

>>> a_dict = {}
>>> try:
...     # Do something with 'key'...
...     a_dict['key']
... except KeyError:
...     a_dict['key'] = 'default value'
...
>>> a_dict
{'key': 'default value'}


Блок try и except в приведенном выше примере перехватывает KeyError всякий раз, когда вы пытаетесь получить доступ к отсутствующему ключу. В предложении except вы создаете key и присваиваете ему значение 'default value'.

Примечание: Если в вашем коде отсутствуют ключи, то вы можете предпочесть использовать блоки try и except (Стиль кодирования EAFP), чтобы перехватить исключение KeyError. Это связано с тем, что код не проверяет наличие каждого ключа и обрабатывает только несколько исключений, если таковые имеются.

С другой стороны, если отсутствующие ключи довольно часто встречаются в вашем коде, то условный оператор ( стиль кодирования LBYL) может быть лучшим выбором, поскольку проверка ключей может быть менее затратной, чем обработка частые исключения.

На данный момент вы узнали, как обрабатывать пропущенные ключи с помощью инструментов, которые вам предлагают dict и Python. Однако примеры, которые вы видели здесь, довольно многословны и их трудно читать. Возможно, они не так просты, как вам хотелось бы. Вот почему Стандартная библиотека Python предоставляет более элегантное, основанное на Python и эффективное решение. Это решение collections.defaultdict, и это то, о чем вы будете говорить с этого момента.

Понимание типа Python defaultdict

Стандартная библиотека Python предоставляет collections,, который представляет собой модуль, реализующий специализированные типы контейнеров. Одним из них является тип Python defaultdict, который является альтернативой dict и специально разработан, чтобы помочь вам с недостающими ключами. defaultdict - это тип Python, который наследуется от dict:

>>> from collections import defaultdict
>>> issubclass(defaultdict, dict)
True


Приведенный выше код показывает, что тип Python defaultdict является подклассом из dict. Это означает, что defaultdict наследует большую часть поведения dict. Таким образом, можно сказать, что defaultdict очень похож на обычный словарь.

Основное различие между defaultdict и dict заключается в том, что при попытке получить доступ или изменить key, которого нет в словаре, по умолчанию используется value автоматически присваивается этому key. Чтобы обеспечить эту функциональность, тип Python defaultdict выполняет две функции:

  1. Это переопределяет .__missing__().
  2. Он добавляет .default_factory, переменную экземпляра, доступную для записи, которая должна быть указана во время создания экземпляра.

Переменная экземпляра .default_factory будет содержать первый аргумент, переданный в defaultdict.__init__(). Этот аргумент может принимать допустимый вызываемый параметр Python или None. Если указан вызываемый параметр, то он будет автоматически вызываться с помощью defaultdict всякий раз, когда вы попытаетесь получить доступ к отсутствующему ключу или изменить его значение.

Примечание: Все остальные аргументы инициализатора класса обрабатываются так, как если бы они были переданы в инициализатор обычного dict, включая аргументы ключевого слова.

Взгляните на то, как вы можете создать и правильно инициализировать defaultdict:

>>> # Correct instantiation
>>> def_dict = defaultdict(list)  # Pass list to .default_factory
>>> def_dict['one'] = 1  # Add a key-value pair
>>> def_dict['missing']  # Access a missing key returns an empty list
[]
>>> def_dict['another_missing'].append(4)  # Modify a missing key
>>> def_dict
defaultdict(<class 'list'>, {'one': 1, 'missing': [], 'another_missing': [4]})


Здесь вы передаете list в .default_factory при создании словаря. Затем вы используете def_dict точно так же, как обычный словарь. Обратите внимание, что когда вы пытаетесь получить доступ к значению, сопоставленному с несуществующим ключом, или изменить его, словарь присваивает ему значение по умолчанию, полученное в результате вызова list().

Имейте в виду, что вы должны передать допустимый вызываемый объект Python в .default_factory, поэтому не забывайте вызывать его, используя круглые скобки, во время инициализации. Это может быть распространенной проблемой, когда вы начинаете использовать тип Python defaultdict. Взгляните на следующий код:

>>> # Wrong instantiation
>>> def_dict = defaultdict(list())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    def_dict = defaultdict(list())
TypeError: first argument must be callable or None


Здесь вы пытаетесь создать defaultdict, передавая list() в .default_factory. Вызов list() вызывает TypeError, который сообщает вам, что первый аргумент должен быть вызываемым или None.

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

Используя тип Python defaultdict

Иногда вы будете использовать изменяемую встроенную коллекцию (a list, dict, или set) as ) значений в ваших словарях Python. В этих случаях вам нужно будет инициализировать ключи перед первым использованием, иначе вы получите KeyError. Вы можете выполнить этот процесс вручную или автоматизировать его с помощью Python defaultdict. В этом разделе вы узнаете, как использовать тип Python defaultdict для решения некоторых распространенных задач программирования:

  • Группировка элементов в коллекции
  • Подсчет предметов в коллекции
  • Накопление значений в коллекции

Вы приведете несколько примеров, в которых используются list, set, int, и float для выполнения операций группировки, подсчета и накопления в пользовательском-удобный и эффективный способ.

Группировка элементов

Типичным использованием типа Python defaultdict является установка для .default_factory значения list, а затем создание словаря, который сопоставляет ключи со списками значений. С помощью этого defaultdict, если вы попытаетесь получить доступ к любому отсутствующему ключу, словарь выполнит следующие действия:

  1. Вызовите list() для создания нового пустого list
  2. Вставьте пустой list в словарь, используя отсутствующий ключ в качестве key
  3. Возвращает ссылку на это list

Это позволяет вам написать код следующим образом:

>>> from collections import defaultdict
>>> dd = defaultdict(list)
>>> dd['key'].append(1)
>>> dd
defaultdict(<class 'list'>, {'key': [1]})
>>> dd['key'].append(2)
>>> dd
defaultdict(<class 'list'>, {'key': [1, 2]})
>>> dd['key'].append(3)
>>> dd
defaultdict(<class 'list'>, {'key': [1, 2, 3]})


Здесь вы создаете Python defaultdict с именем dd и передаете list в .default_factory. Обратите внимание, что даже если key не определено, вы можете добавить к нему значения, не получая KeyError. Это потому, что dd автоматически вызывает .default_factory, чтобы сгенерировать значение по умолчанию для отсутствующего key.

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

Department Employee Name
Sales John Doe
Sales Martin Smith
Accounting Jane Doe
Marketing Elizabeth Smith
Marketing Adam Doe

Используя эти данные, вы создаете начальный list из tuple объектов, подобных следующим:

dep = [('Sales', 'John Doe'),
       ('Sales', 'Martin Smith'),
       ('Accounting', 'Jane Doe'),
       ('Marketing', 'Elizabeth Smith'),
       ('Marketing', 'Adam Doe')]


Теперь вам нужно создать словарь, который сгруппирует сотрудников по отделам. Для этого вы можете использовать defaultdict следующим образом:

from collections import defaultdict

dep_dd = defaultdict(list)
for department, employee in dep:
    dep_dd[department].append(employee)


Здесь вы создаете defaultdict с именем dep_dd и используете for цикл для итерации по вашему списку dep. Инструкция dep_dd[department].append(employee) создает ключи для отделов, инициализирует их в виде пустого списка, а затем добавляет сотрудников в каждый отдел. Как только вы запустите этот код, ваш dep_dd будет выглядеть примерно так:

defaultdict(<class 'list'>, {'Sales': ['John Doe', 'Martin Smith'],
                             'Accounting' : ['Jane Doe'],
                             'Marketing': ['Elizabeth Smith', 'Adam Doe']})


В этом примере вы группируете сотрудников по их отделам, используя defaultdict, а для .default_factory задается значение list. Чтобы сделать это с помощью обычного словаря, вы можете использовать dict.setdefault() следующим образом:

dep_d = dict()
for department, employee in dep:
    dep_d.setdefault(department, []).append(employee)


Этот код прост в использовании, и вы довольно часто будете сталкиваться с подобным кодом в своей работе в качестве программиста на Python. Однако версия defaultdict, возможно, более удобочитаема, а для больших наборов данных она также может быть намного быстрее и эффективнее. Итак, если вас беспокоит скорость, то вам следует рассмотреть возможность использования defaultdict вместо стандартного dict.

Группировка уникальных элементов

Продолжайте работать с данными о подразделениях и сотрудниках из предыдущего раздела. После некоторой обработки вы обнаружите, что несколько сотрудников были дублированы в базе данных по ошибке. Вам необходимо очистить данные и удалить дублирующихся сотрудников из вашего словаря dep_dd. Чтобы сделать это, вы можете использовать set в качестве .default_factory и переписать свой код следующим образом:

dep = [('Sales', 'John Doe'),
       ('Sales', 'Martin Smith'),
       ('Accounting', 'Jane Doe'),
       ('Marketing', 'Elizabeth Smith'),
       ('Marketing', 'Elizabeth Smith'),
       ('Marketing', 'Adam Doe'),
       ('Marketing', 'Adam Doe'),
       ('Marketing', 'Adam Doe')]

dep_dd = defaultdict(set)
for department, employee in dep:
    dep_dd[department].add(employee)


В этом примере для параметра .default_factory задано значение set. Наборы - это коллекций уникальных объектов, что означает, что вы не можете создать set с повторяющимися элементами. Это действительно интересная особенность наборов, которая гарантирует, что в вашем окончательном словаре не будет повторяющихся элементов.

Подсчет предметов

Если вы установите для .default_factory значение int,, то ваш defaultdict будет полезен для подсчета элементов в последовательности или коллекции. Когда вы вызываете int() без аргументов, функция возвращает 0, что является типичным значением, которое вы бы использовали для инициализации счетчика.

Чтобы продолжить рассмотрение примера с базой данных компании, предположим, вы хотите создать словарь, который подсчитывает количество сотрудников в каждом отделе. В этом случае вы можете запрограммировать что-то вроде этого:

>>> from collections import defaultdict
>>> dep = [('Sales', 'John Doe'),
...        ('Sales', 'Martin Smith'),
...        ('Accounting', 'Jane Doe'),
...        ('Marketing', 'Elizabeth Smith'),
...        ('Marketing', 'Adam Doe')]
>>> dd = defaultdict(int)
>>> for department, _ in dep:
...     dd[department] += 1
>>> dd
defaultdict(<class 'int'>, {'Sales': 2, 'Accounting': 1, 'Marketing': 2})


Здесь для .default_factory задается значение int. При вызове int() без аргумента возвращаемое значение равно 0. Вы можете использовать это значение по умолчанию, чтобы начать подсчет сотрудников, работающих в каждом отделе. Для корректной работы этого кода вам нужен чистый набор данных. В нем не должно быть повторяющихся данных. В противном случае вам нужно будет отфильтровать повторяющихся сотрудников.

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

>>> from collections import defaultdict
>>> s = 'mississippi'
>>> dd = defaultdict(int)
>>> for letter in s:
...     dd[letter] += 1
...
>>> dd
defaultdict(<class 'int'>, {'m': 1, 'i': 4, 's': 4, 'p': 2})


В приведенном выше коде вы создаете defaultdict с .default_factory, равным int. Это устанавливает значение по умолчанию для любого данного ключа равным 0. Затем вы используете цикл for для пересечения строки s и используете операцию расширенного присваивания , чтобы добавить 1 к счетчику на каждой итерации. Клавишами dd будут буквы в mississippi.

Примечание: Расширенные операторы присваивания в Python являются удобным сокращением для обычных операций.

Взгляните на следующие примеры:

  • var += 1 эквивалентно var = var + 1
  • var -= 1 эквивалентно var = var - 1
  • var *= 1 эквивалентно var = var * 1

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

Поскольку подсчет является относительно распространенной задачей в программировании, класс, подобный словарю Python collections.Counter, специально разработан для подсчета элементов в последовательности. Используя Counter, вы можете записать пример mississippi следующим образом:

>>> from collections import Counter
>>> counter = Counter('mississippi')
>>> counter
Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})


В этом случае Counter сделает всю работу за вас! Вам нужно только ввести последовательность, и словарь подсчитает свои элементы, сохранив их как ключи, а количество - как значения. Обратите внимание, что этот пример работает, потому что строки Python также являются типом последовательности.

Накапливаемые значения

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

Products July August September
Books 1250.00 1300.00 1420.00
Tutorials 560.00 630.00 750.00
Courses 2500.00 2430.00 2750.00

Далее вы обрабатываете данные с помощью Python и получаете следующие list из tuple объектов:

incomes = [('Books', 1250.00),
           ('Books', 1300.00),
           ('Books', 1420.00),
           ('Tutorials', 560.00),
           ('Tutorials', 630.00),
           ('Tutorials', 750.00),
           ('Courses', 2500.00),
           ('Courses', 2430.00),
           ('Courses', 2750.00),]


Используя эти данные, вы хотите рассчитать общий доход от каждого продукта. Чтобы сделать это, вы можете использовать Python defaultdict с float как .default_factory, а затем закодировать что-то вроде этого:

 1from collections import defaultdict
 2
 3dd = defaultdict(float)
 4for product, income in incomes:
 5    dd[product] += income
 6
 7for product, income in dd.items():
 8    print(f'Total income for {product}: ${income:,.2f}')


Вот что делает этот код:

  • В строке 1 вы импортируете тип Python defaultdict.
  • В строке 3 вы создаете объект defaultdict, для которого значение .default_factory равно float.
  • В строке 4 вы определяете цикл for для перебора элементов incomes.
  • В строке 5 вы используете расширенную операцию присвоения (+=) для накопления доходов по каждому продукту в словаре.

Второй цикл выполняет итерацию по элементам dd и выводит результаты на экран.

Примечание: Если вы хотите углубиться в итерацию по словарю, ознакомьтесь с Как выполнять итерацию по словарю в Python.

Если вы поместите весь этот код в файл с именем incomes.py и запустите его из командной строки, то получите следующий результат:

$ python3 incomes.py
Total income for Books: $3,970.00
Total income for Tutorials: $1,940.00
Total income for Courses: $7,680.00


Теперь у вас есть сводная информация о доходах от каждого продукта, так что вы можете принимать решения о том, какой стратегии придерживаться для увеличения общего дохода вашего сайта.

Погружаясь глубже в defaultdict

Итак, вы узнали, как использовать тип Python defaultdict, написав несколько практических примеров. На этом этапе вы можете углубиться в реализацию типа и другие рабочие детали. Это то, что вы будете рассматривать в следующих нескольких разделах.

defaultdict против dict

Чтобы вы лучше понимали тип Python defaultdict, хорошим упражнением было бы сравнить его с его суперклассом dict. Если вы хотите узнать методы и атрибуты, характерные для типа Python defaultdict, то вы можете запустить следующую строку кода:

>>> set(dir(defaultdict)) - set(dir(dict))
{'__copy__', 'default_factory', '__missing__'}


В приведенном выше коде вы используете dir(), чтобы получить список допустимых атрибутов для dict и defaultdict. Затем вы используете set difference, чтобы получить набор методов и атрибутов, которые вы можете найти только в defaultdict. Как вы можете видеть, различия между этими двумя классами заключаются в следующем. У вас есть два метода и один атрибут экземпляра. В следующей таблице показано, для чего предназначены методы и атрибут:

Method or Attribute Description
.__copy__() Provides support for copy.copy()
.default_factory Holds the callable invoked by .__missing__() to automatically provide default values for missing keys
.__missing__(key) Gets called when .__getitem__() can’t find key

В таблице выше вы можете увидеть методы и атрибуты, которые отличают defaultdict от обычного dict. Остальные методы одинаковы в обоих классах.

Примечание: Если вы инициализируете defaultdict с помощью допустимого вызываемого параметра, то вы не получите KeyError при попытке получить доступ к отсутствующему ключу. Любой несуществующий ключ получает значение, возвращаемое с помощью .default_factory.

Кроме того, вы можете заметить, что defaultdict равно dict с теми же элементами:

>>> std_dict = dict(numbers=[1, 2, 3], letters=['a', 'b', 'c'])
>>> std_dict
{'numbers': [1, 2, 3], 'letters': ['a', 'b', 'c']}
>>> def_dict = defaultdict(list, numbers=[1, 2, 3], letters=['a', 'b', 'c'])
>>> def_dict
defaultdict(<class 'list'>, {'numbers': [1, 2, 3], 'letters': ['a', 'b', 'c']})
>>> std_dict == def_dict
True


Здесь вы создаете обычный словарь std_dict с некоторыми произвольными элементами. Затем вы создаете defaultdict с теми же элементами. Если вы проверите оба словаря на равенство содержимого, то увидите, что они равны.

defaultdict.default_factory

Первым аргументом типа Python defaultdict должен быть вызываемый, который не принимает аргументов и возвращает значение. Этот аргумент присваивается атрибуту экземпляра .default_factory. Для этого вы можете использовать любой вызываемый объект, включая функции, методы, классы, объекты типа или любой другой допустимый вызываемый объект. Значение по умолчанию .default_factory равно None.

Если вы создадите экземпляр defaultdict без передачи значения в .default_factory, то словарь будет вести себя как обычный dict, а обычный KeyError будет вызван как отсутствующий попытки поиска или изменения ключа:

>>> from collections import defaultdict
>>> dd = defaultdict()
>>> dd['missing_key']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    dd['missing_key']
KeyError: 'missing_key'


Здесь вы создаете экземпляр типа Python defaultdict без аргументов. В этом случае экземпляр ведет себя как стандартный словарь. Итак, если вы попытаетесь получить доступ к отсутствующему ключу или изменить его, то получите обычное сообщение KeyError. С этого момента вы можете использовать dd как обычный словарь Python, и, если вы не назначите новый вызываемый элемент для .default_factory, вы не сможете использовать возможности defaultdict для автоматической обработки отсутствующих ключей.

Если вы передадите None в качестве первого аргумента defaultdict, то экземпляр будет вести себя так же, как вы видели в приведенном выше примере. Это потому, что .default_factory по умолчанию имеет значение None, поэтому обе инициализации эквивалентны. С другой стороны, если вы передадите допустимый вызываемый объект в .default_factory, то вы можете использовать его для обработки отсутствующих ключей удобным для пользователя способом. Вот пример, в котором вы передаете list в .default_factory:

>>> dd = defaultdict(list, letters=['a', 'b', 'c'])
>>> dd.default_factory
<class 'list'>
>>> dd
defaultdict(<class 'list'>, {'letters': ['a', 'b', 'c']})
>>> dd['numbers']
[]
>>> dd
defaultdict(<class 'list'>, {'letters': ['a', 'b', 'c'], 'numbers': []})
>>> dd['numbers'].append(1)
>>> dd
defaultdict(<class 'list'>, {'letters': ['a', 'b', 'c'], 'numbers': [1]})
>>> dd['numbers'] += [2, 3]
>>> dd
defaultdict(<class 'list'>, {'letters': ['a', 'b', 'c'], 'numbers': [1, 2, 3]})


В этом примере вы создаете Python defaultdict с именем dd, затем используете list в качестве его первого аргумента. Второй аргумент называется letters и содержит список букв. Вы видите, что .default_factory теперь содержит объект list, который будет вызываться, когда вам нужно будет указать значение по умолчанию value для любого отсутствующего ключа.

Обратите внимание, что при попытке доступа numbers, dd проверяется, есть ли в словаре numbers. Если нет, то вызывается .default_factory(). Поскольку .default_factory содержит объект list, возвращаемый value является пустым списком ([]).

Теперь, когда dd['numbers'] инициализируется пустым list, вы можете использовать .append() для добавления элементов в list. Вы также можете использовать расширенный оператор присваивания (+=) для объединения списков [1] и [2, 3]. Таким образом, вы можете обрабатывать недостающие ключи более простым и эффективным способом.

С другой стороны, если вы передадите не вызываемый объект в инициализатор типа Python defaultdict, то вы получите TypeError как в следующем коде:

>>> defaultdict(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    defaultdict(0)
TypeError: first argument must be callable or None


Здесь вы переходите от 0 к .default_factory. Поскольку 0 не является вызываемым объектом, вы получаете TypeError, сообщающий вам, что первый аргумент должен быть вызываемым или None. В противном случае defaultdict не сработает.

Имейте в виду, что .default_factory вызывается только из .__getitem__(), а не из других методов. Это означает, что если dd является defaultdict, а key является отсутствующим ключом, то dd[key] вызовет .default_factory, чтобы указать значение по умолчанию value, но dd.get(key) по-прежнему возвращает None вместо значения, которое могло бы предоставить .default_factory. Это потому, что .get() не вызывает .__getitem__() для получения key.

Взгляните на следующий код:

>>> dd = defaultdict(list)
>>> # Calls dd.__getitem__('missing')
>>> dd['missing']
[]
>>> # Don't call dd.__getitem__('another_missing')
>>> print(dd.get('another_missing'))
None
>>> dd
defaultdict(<class 'list'>, {'missing': []})


В этом фрагменте кода вы можете видеть, что dd.get() возвращает None, а не значение по умолчанию, которое было бы указано в .default_factory. Это потому, что .default_factory вызывается только из .__missing__(), который не вызывается с помощью .get().

Обратите внимание, что вы также можете добавлять произвольные значения в Python defaultdict. Это означает, что вы не ограничены значениями того же типа, что и значения, сгенерированные с помощью .default_factory. Вот пример:

>>> dd = defaultdict(list)
>>> dd
defaultdict(<class 'list'>, {})
>>> dd['string'] = 'some string'
>>> dd
defaultdict(<class 'list'>, {'string': 'some string'})
>>> dd['list']
[]
>>> dd
defaultdict(<class 'list'>, {'string': 'some string', 'list': []})


Здесь вы создаете defaultdict и передаете объект list в .default_factory. При этом значения по умолчанию будут пустыми списками. Однако вы можете свободно добавить новый ключ, содержащий значения другого типа. Так обстоит дело с ключом string, который содержит объект str вместо объекта list.

Наконец, вы всегда можете изменить или обновить вызываемый, который вы изначально присвоили .default_factory, точно так же, как вы бы поступили с любым атрибутом экземпляра:

>>> dd.default_factory = str
>>> dd['missing_key']
''


В приведенном выше коде вы меняете .default_factory с list на str. Теперь всякий раз, когда вы пытаетесь получить доступ к отсутствующему ключу, вашим значением по умолчанию будет пустая строка ('').

В зависимости от ваших вариантов использования типа Python defaultdict, вам может потребоваться заморозить словарь после завершения его создания и сделать его доступным только для чтения. Чтобы сделать это, вы можете установить для .default_factory значение None после завершения заполнения словаря. Таким образом, ваш словарь будет вести себя как стандартный dict, что означает, что у вас больше не будет автоматически сгенерированных значений по умолчанию.

defaultdict против dict.setdefault()

Как вы видели ранее, dict содержит .setdefault(), что позволит вам на лету присваивать значения отсутствующим ключам. В отличие от этого, с помощью defaultdict вы можете указать значение по умолчанию заранее при инициализации контейнера. Вы можете использовать .setdefault() для назначения значений по умолчанию следующим образом:

>>> d = dict()
>>> d.setdefault('missing_key', [])
[]
>>> d
{'missing_key': []}


В этом коде вы создаете обычный словарь, а затем используете .setdefault(), чтобы присвоить значение ([]) ключу missing_key, который еще не был определен.

Примечание: Вы можете назначить любой тип объекта Python, используя .setdefault(). Это важное отличие по сравнению с defaultdict, если учесть, что defaultdict принимает только вызываемый или None.

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

>>> from collections import defaultdict
>>> dd = defaultdict(list)
>>> dd['missing_key']
[]
>>> dd
defaultdict(<class 'list'>, {'missing_key': []})


Здесь вы сначала импортируете тип Python defaultdict из collections. Затем вы создаете defaultdict и передаете list в .default_factory. Когда вы пытаетесь получить доступ к отсутствующему ключу, defaultdict внутренне вызывает .default_factory(), который содержит ссылку на list, и присваивает результирующее значение (пустое list) missing_key.

Код в двух приведенных выше примерах выполняет ту же работу, но версия defaultdict, возможно, более читабельна, удобна для пользователя, основана на Python и понятна.

Примечание: Вызов встроенного типа, такого как list, set, dict, str, int, или float, вернет пустой объект или ноль для числовых типов.

Взгляните на следующие примеры кода:

>>> list()
[]
>>> set()
set([])
>>> dict()
{}
>>> str()
''
>>> float()
0.0
>>> int()
0


В этом коде вы вызываете некоторые встроенные типы без аргументов и получаете пустой объект или ноль для числовых типов.

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

# Filename: exec_time.py

from collections import defaultdict
from timeit import timeit

animals = [('cat', 1), ('rabbit', 2), ('cat', 3), ('dog', 4), ('dog', 1)]
std_dict = dict()
def_dict = defaultdict(list)

def group_with_dict():
    for animal, count in animals:
        std_dict.setdefault(animal, []).append(count)
    return std_dict

def group_with_defaultdict():
    for animal, count in animals:
        def_dict[animal].append(count)
    return def_dict

print(f'dict.setdefault() takes {timeit(group_with_dict)} seconds.')
print(f'defaultdict takes {timeit(group_with_defaultdict)} seconds.')


Если вы запустите скрипт из командной строки вашей системы, то получите что-то вроде этого:

$ python3 exec_time.py
dict.setdefault() takes 1.0281260240008123 seconds.
defaultdict takes 0.6704721650003194 seconds.


Здесь используется timeit.timeit() для измерения времени выполнения group_with_dict() и group_with_defaultdict(). Эти функции выполняют эквивалентные действия, но первая использует dict.setdefault(), а вторая - defaultdict. Время измерения будет зависеть от используемого вами оборудования, но здесь вы можете увидеть, что defaultdict быстрее, чем dict.setdefault(). Это различие может стать более важным по мере увеличения набора данных.

Кроме того, вам нужно учитывать, что создание обычного dict может быть быстрее, чем создание defaultdict. Взгляните на этот код:

>>> from timeit import timeit
>>> from collections import defaultdict
>>> print(f'dict() takes {timeit(dict)} seconds.')
dict() takes 0.08921320698573254 seconds.
>>> print(f'defaultdict() takes {timeit(defaultdict)} seconds.')
defaultdict() takes 0.14101867799763568 seconds.


На этот раз вы используете timeit.timeit() для измерения времени выполнения dict и defaultdict экземпляров. Обратите внимание, что создание dict занимает почти вдвое меньше времени, чем создание defaultdict. Возможно, это не проблема, если учесть, что в реальном коде вы обычно создаете экземпляр defaultdict только один раз.

Также обратите внимание, что по умолчанию timeit.timeit() запускает ваш код миллион раз. Именно по этой причине std_dict и def_dict определены вне рамок group_with_dict() и group_with_defaultdict() в exec_time.py. В противном случае на измерение времени будет влиять время создания экземпляра dict и defaultdict.

На этом этапе у вас может возникнуть представление о том, когда следует использовать defaultdict вместо обычного dict. Вот три вещи, которые следует учитывать:

  1. Если ваш код в значительной степени основан на словарях и вы постоянно сталкиваетесь с отсутствующими ключами, то вам следует рассмотреть возможность использования defaultdict вместо обычного dict.

  2. Если элементы вашего словаря необходимо инициализировать постоянным значением по умолчанию, то вам следует рассмотреть возможность использования defaultdict вместо dict.

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

Вы можете руководствоваться приведенными выше рекомендациями при принятии решения об использовании dict или defaultdict.

defaultdict.__missing__()

За кулисами тип Python defaultdict работает путем вызова .default_factory, чтобы указать значения по умолчанию для отсутствующих ключей. Механизмом, который делает это возможным, является .__missing__(), специальный метод , поддерживаемый всеми стандартными типами отображения, включая dict и defaultdict.

Примечание: Обратите внимание, что .__missing__() автоматически вызывается .__getitem__() для обработки отсутствующих ключей, а .__getitem__() автоматически вызывается Python в то же время для операций подписки, таких как d[key].

Итак, как работает .__missing__()? Если вы установите для .default_factory значение None, то .__missing__() вызовет KeyError с key в качестве аргумента. В противном случае .default_factory вызывается без аргументов, чтобы предоставить значение по умолчанию value для данного key. Это value вставляется в словарь и, наконец, возвращается. Если вызов .default_factory вызывает исключение, то это исключение распространяется без изменений.

Следующий код показывает жизнеспособную реализацию Python для .__missing__():

 1def __missing__(self, key):
 2    if self.default_factory is None:
 3        raise KeyError(key)
 4    if key not in self:
 5        self[key] = self.default_factory()
 6    return self[key]


Вот что делает этот код:

  • В строке 1 вы определяете метод и его сигнатуру.
  • В строках 2 и 3 вы проверяете, соответствует ли .default_factory None. Если это так, то вы приводите KeyError с key в качестве аргумента.
  • В строках 4 и 5 вы проверяете, нет ли key в словаре. Если это не так, то вы вызываете .default_factory и присваиваете возвращаемое значение параметру key.
  • В строке 6 вы возвращаете key, как и ожидалось.

Имейте в виду, что наличие .__missing__() в отображении не влияет на поведение других методов, которые ищут ключи, таких как .get() или .__contains__(), которые реализуют in оператор. Это потому, что .__missing__() вызывается только .__getitem__(), когда запрошенный key не найден в словаре. Все, что возвращает или повышает значение .__missing__(), затем возвращается или повышается с помощью .__getitem__().

Теперь, когда вы рассмотрели альтернативную реализацию Python для .__missing__(), было бы неплохо попробовать эмулировать defaultdict с помощью некоторого кода на Python. Это то, что вы будете делать в следующем разделе.

Эмуляция типа Python defaultdict

В этом разделе вы будете кодировать класс Python, который будет вести себя так же, как defaultdict. Для этого вы создадите подкласс collections.UserDict, а затем добавите .__missing__(). Кроме того, вам нужно добавить атрибут экземпляра с именем .default_factory, который будет содержать вызываемый параметр для генерации значений по умолчанию по требованию. Вот фрагмент кода, который эмулирует большую часть поведения типа Python defaultdict:

 1import collections
 2
 3class my_defaultdict(collections.UserDict):
 4    def __init__(self, default_factory=None, *args, **kwargs):
 5        super().__init__(*args, **kwargs)
 6        if not callable(default_factory) and default_factory is not None:
 7            raise TypeError('first argument must be callable or None')
 8        self.default_factory = default_factory
 9
10    def __missing__(self, key):
11        if self.default_factory is None:
12            raise KeyError(key)
13        if key not in self:
14            self[key] = self.default_factory()
15        return self[key]


Вот как работает этот код:

  • В строке 1 вы импортируете collections, чтобы получить доступ к UserDict.

  • В строке 3 вы создаете класс, который создает подклассы UserDict.

  • В строке 4 вы определяете инициализатор класса .__init__(). Этот метод принимает аргумент с именем default_factory для хранения вызываемого параметра, который вы будете использовать для генерации значений по умолчанию. Обратите внимание, что default_factory по умолчанию равно None, как и в defaultdict. Вам также понадобятся *args и **kwargs для имитации обычного поведения обычного пользователя. dict.

  • В строке 5 вы вызываете суперкласс .__init__(). Это означает, что вы вызываете UserDict.__init__() и передаете ему *args и **kwargs.

  • В строке 6 вы сначала проверяете, является ли default_factory допустимым вызываемым объектом. В этом случае вы используете callable(object),, которая является встроенной функцией, которая возвращает True, если object является вызываемой, и в противном случае возвращает False. Эта проверка гарантирует, что вы можете вызвать .default_factory(), если вам нужно сгенерировать значение по умолчанию value для любого отсутствующего key. Затем вы проверяете, не является ли .default_factory None.

  • В строке 7 вы создаете TypeError точно так же, как это сделал бы обычный dict, если default_factory равно None.

  • В строке 8 вы инициализируете .default_factory.

  • В строке 10 вы определяете .__missing__(), который реализован так, как вы видели ранее. Напомним, что .__missing__() автоматически вызывается .__getitem__(), когда данного key нет в словаре.

Если у вас есть желание почитать какой-нибудь код на C, то вы можете взглянуть на полный код для Python defaultdict Введите Исходный код CPython.

Теперь, когда вы закончили кодировать этот класс, вы можете протестировать его, поместив код в скрипт на Python с именем my_dd.py и импортировав его из интерактивного сеанса. Вот пример:

>>> from my_dd import my_defaultdict
>>> dd_one = my_defaultdict(list)
>>> dd_one
{}
>>> dd_one['missing']
[]
>>> dd_one
{'missing': []}
>>> dd_one.default_factory = int
>>> dd_one['another_missing']
0
>>> dd_one
{'missing': [], 'another_missing': 0}
>>> dd_two = my_defaultdict(None)
>>> dd_two['missing']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    dd_two['missing']
  File "/home/user/my_dd.py", line 10,
 in __missing__
    raise KeyError(key)
KeyError: 'missing'


Здесь вы сначала импортируете my_defaultdict из my_dd. Затем вы создаете экземпляр my_defaultdict и передаете list в .default_factory. Если вы пытаетесь получить доступ к ключу с помощью операции подписки, например, dd_one['missing'], то Python автоматически вызывает .__getitem__(). Если ключа нет в словаре, то вызывается .__missing__(), который генерирует значение по умолчанию путем вызова .default_factory().

Вы также можете изменить вызываемый объект, присвоенный .default_factory, используя обычную операцию присвоения, как в dd_one.default_factory = int. Наконец, если вы передадите None в .default_factory, то при попытке получить недостающий ключ вы получите KeyError.

Примечание: Поведение defaultdict по сути такое же, как и у этого эквивалента на Python. Однако вскоре вы заметите, что ваша реализация на Python печатается не как настоящая defaultdict, а как стандартная dict. Вы можете изменить эту информацию, переопределив .__str__() и .__repr__().

Возможно, вам интересно, почему в этом примере вы создаете подкласс collections.UserDict вместо обычного dict. Основная причина этого заключается в том, что подклассы встроенных типов могут быть подвержены ошибкам, поскольку C-код встроенных модулей, по-видимому, не всегда последовательно вызывает специальные методы, переопределяемые пользователем.

Вот пример, который показывает некоторые проблемы, с которыми вы можете столкнуться при создании подклассов dict:

>>> class MyDict(dict):
...     def __setitem__(self, key, value):
...         super().__setitem__(key, None)
...
>>> my_dict = MyDict(first=1)
>>> my_dict
{'first': 1}
>>> my_dict['second'] = 2
>>> my_dict
{'first': 1, 'second': None}
>>> my_dict.setdefault('third', 3)
3
>>> my_dict
{'first': 1, 'second': None, 'third': 3}


В этом примере вы создаете MyDict, который является классом, являющимся подклассом dict. Ваша реализация .__setitem__() всегда устанавливает значения равными None. Если вы создадите экземпляр MyDict и передадите аргумент ключевого слова его инициализатору, то вы заметите, что класс не вызывает ваш .__setitem__() для обработки присваивания. Вы знаете это, потому что ключ first не был назначен None.

В отличие от этого, если вы запустите операцию подписки, подобную my_dict['second'] = 2, то вы заметите, что для second задано значение None, а не 2. Итак, на этот раз вы можете сказать, что операции подписки вызывают ваш пользовательский .__setitem__(). Наконец, обратите внимание, что .setdefault() также не вызывает .__setitem__(), потому что ваш ключ third заканчивается значением 3.

UserDict не наследуется от dict, но имитирует поведение стандартного словаря. У класса есть внутренний dict экземпляр с именем .data, который используется для хранения содержимого словаря. UserDict является более надежным классом, когда дело доходит до создания пользовательских сопоставлений. Если вы используете UserDict, то вы избежите проблем, с которыми сталкивались ранее. Чтобы доказать это, вернитесь к коду для my_defaultdict и добавьте следующий метод:

 1class my_defaultdict(collections.UserDict):
 2    # Snip
 3    def __setitem__(self, key, value):
 4        print('__setitem__() gets called')
 5        super().__setitem__(key, None)


Здесь вы добавляете пользовательский .__setitem__(), который вызывает суперкласс .__setitem__(), который всегда устанавливает значение равным None. Обновите этот код в своем скрипте my_dd.py и импортируйте его из интерактивного сеанса следующим образом:

>>> from my_dd import my_defaultdict
>>> my_dict = my_defaultdict(list, first=1)
__setitem__() gets called
>>> my_dict
{'first': None}
>>> my_dict['second'] = 2
__setitem__() gets called
>>> my_dict
{'first': None, 'second': None}


В этом случае, когда вы создаете экземпляр my_defaultdict и передаете first в инициализатор класса, вызывается ваш пользовательский __setitem__(). Кроме того, когда вы присваиваете значение ключу second, __setitem__(), он также вызывается. Теперь у вас есть my_defaultdict, который последовательно вызывает ваши пользовательские специальные методы. Обратите внимание, что теперь все значения в словаре равны None.

Передача аргументов в .default_factory

Как вы видели ранее, .default_factory должно быть присвоено вызываемому объекту, который не принимает аргументов и возвращает значение. Это значение будет использоваться для указания значения по умолчанию для любого отсутствующего ключа в словаре. Даже когда .default_factory не должен принимать аргументы, Python предлагает несколько приемов, которые вы можете использовать, если вам нужно предоставить ему аргументы. В этом разделе вы познакомитесь с двумя инструментами Python, которые могут служить для этой цели:

  1. lambda
  2. functools.partial()

С помощью этих двух инструментов вы можете добавить дополнительную гибкость типу Python defaultdict. Например, вы можете инициализировать defaultdict с помощью вызываемого параметра, который принимает аргумент, и после некоторой обработки вы можете обновить вызываемый параметр с помощью нового аргумента, чтобы изменить значение по умолчанию для ключей, которые вы будете создавать с этого момента.

Используя lambda

Гибким способом передачи аргументов в .default_factory является использование lambda. Предположим, вы хотите создать функцию для генерации значений по умолчанию в defaultdict. Функция выполняет некоторую обработку и возвращает значение, но для корректной работы функции необходимо передать аргумент. Вот пример:

>>> def factory(arg):
...     # Do some processing here...
...     result = arg.upper()
...     return result
...
>>> def_dict = defaultdict(lambda: factory('default value'))
>>> def_dict['missing']
'DEFAULT VALUE'


В приведенном выше коде вы создаете функцию с именем factory(). Функция принимает аргумент, выполняет некоторую обработку и возвращает конечный результат. Затем вы создаете defaultdict и используете lambda, чтобы передать строку 'default value' в factory(). При попытке получить доступ к отсутствующему ключу выполняются следующие действия:

  1. словарь def_dict называет ее .default_factory, которое содержит ссылку на lambda функция.
  2. Вызывается функция lambda, которая возвращает значение, полученное в результате вызова factory() с 'default value' в качестве аргумента.

Если вы работаете с def_dict и вам вдруг нужно изменить аргумент на factory(), то вы можете сделать что-то вроде этого:

>>> def_dict.default_factory = lambda: factory('another default value')
>>> def_dict['another_missing']
'ANOTHER DEFAULT VALUE'


На этот раз factory() принимает новый строковый аргумент ('another default value'). Отныне, если вы попытаетесь получить доступ к отсутствующему ключу или изменить его, вы получите новое значение по умолчанию, которое представляет собой строку 'ANOTHER DEFAULT VALUE'.

Наконец, вы, возможно, столкнетесь с ситуацией, когда вам понадобится значение по умолчанию, отличное от 0 или []. В этом случае вы также можете использовать lambda для создания другого значения по умолчанию. Например, предположим, что у вас есть list целых чисел, и вам нужно вычислить суммарное произведение каждого числа. Затем вы можете использовать defaultdict вместе с lambda следующим образом:

>>> from collections import defaultdict
>>> lst = [1, 1, 2, 1, 2, 2, 3, 4, 3, 3, 4, 4]
>>> def_dict = defaultdict(lambda: 1)
>>> for number in lst:
...     def_dict[number] *= number
...
>>> def_dict
defaultdict(<function <lambda> at 0x...70>, {1: 1, 2: 8, 3: 27, 4: 64})


Здесь вы используете lambda для указания значения по умолчанию, равного 1. Используя это начальное значение, вы можете рассчитать суммарное произведение каждого числа в lst. Обратите внимание, что вы не можете получить тот же результат, используя int, потому что значение по умолчанию, возвращаемое int, всегда равно 0, что не является хорошим начальным значением для операций умножения, которые вам нужно выполнить здесь.

Используя functools.partial()

functools.partial(func, *args, **keywords) это функция, которая возвращает объект partial. Когда вы вызываете этот объект с позиционными аргументами (args) и ключевыми словами (keywords),, он ведет себя аналогично вызову func(*args, **keywords). Вы можете воспользоваться преимуществами такого поведения partial() и использовать его для передачи аргументов в .default_factory в Python defaultdict. Вот пример:

>>> def factory(arg):
...     # Do some processing here...
...     result = arg.upper()
...     return result
...
>>> from functools import partial
>>> def_dict = defaultdict(partial(factory, 'default value'))
>>> def_dict['missing']
'DEFAULT VALUE'
>>> def_dict.default_factory = partial(factory, 'another default value')
>>> def_dict['another_missing']
'ANOTHER DEFAULT VALUE'


Здесь вы создаете Python defaultdict и используете partial() в качестве аргумента для .default_factory. Обратите внимание, что вы также можете обновить .default_factory, чтобы использовать другой аргумент для вызываемого factory(). Такое поведение может добавить большую гибкость вашим объектам defaultdict.

Заключение

Тип Python defaultdict представляет собой словарную структуру данных, предоставляемую стандартной библиотекой Python в модуле под названием collections. Класс наследуется от dict, и его основная дополнительная функциональность заключается в предоставлении значений по умолчанию для отсутствующих ключей. В этом руководстве вы узнали, как использовать тип Python defaultdict для обработки отсутствующих ключей в словаре.

Теперь вы можете:

  • Создайте и используйте Python defaultdict для обработки недостающих ключей
  • Решать реальные задачи, связанные с группировкой, подсчетом и накоплением операций
  • Знать различия в реализации между defaultdict и dict
  • Решите, когда и почему использовать Python defaultdict, а не стандартный dict

Тип Python defaultdict - это удобная и эффективная структура данных, которая предназначена для того, чтобы помочь вам, когда вы имеете дело с отсутствующими ключами в словаре. Попробуйте и сделайте свой код более быстрым, удобочитаемым и основанным на Python!

<статус завершения article-slug="python-defaultdict" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-defaultdict/bookmark/" статус завершения data-api-article-url="/api/v1/articles/python-defaultdict/завершение_статуса/"> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0 Использование типа Python defaultdict для обработки отсутствующих ключей" email-subject="Статья о Python для вас" twitter-text="Интересно #Python статья от @realpython:" url="https://realpython.com/python-defaultdict /" url-title="Использование типа Python defaultdict для обработки отсутствующих ключей">

Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Посмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Обработка пропущенных ключей с помощью типа Python defaultdict

Back to Top