Использование типа Python defaultdict для обработки отсутствующих ключей
Оглавление
- Обработка отсутствующих ключей в словарях
- Понимание типа Python defaultdict
- Использование типа Python defaultdict по умолчанию
- Углубляясь в defaultdict
- Эмуляция типа Python defaultdict
- Передача аргументов в файл .default_factory
- Заключение
Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой 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 (Курс)
- Как выполнить итерацию по словарю в Python
Бесплатный бонус: Нажмите здесь, чтобы получить шпаргалку по Python и изучить основы Python 3, такие как работа с типами данных, словари, списки и функции Python.
Обработка пропущенных ключей в словарях
Распространенной проблемой, с которой вы можете столкнуться при работе со словарями Python, является как обрабатывать отсутствующие ключи. Если ваш код в значительной степени основан на словарях или если вы постоянно создаете словари "на лету", то вскоре вы заметите, что работа с частыми KeyError исключениями может сильно раздражать и добавлять дополнительную сложность вашему коду. Со словарями Python у вас есть как минимум четыре доступных способа обработки отсутствующих ключей:
- Использовать
.setdefault() - Использовать
.get() - Используйте идиому
key in dict - Используйте блок
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,, который представляет собой модуль, реализующий специализированные типы контейнеров. Одним из них является тип Pythondefaultdict, который является альтернативойdictи специально разработан, чтобы помочь вам с недостающими ключами.defaultdict- это тип Python, который наследуется отdict:>>> from collections import defaultdict >>> issubclass(defaultdict, dict) Trueпредварительно> кодовый блок>Приведенный выше код показывает, что тип Python
defaultdictявляется подклассом изdict. Это означает, чтоdefaultdictнаследует большую часть поведенияdict. Таким образом, можно сказать, чтоdefaultdictочень похож на обычный словарь.Основное различие между
defaultdictиdictзаключается в том, что при попытке получить доступ или изменитьkey, которого нет в словаре, по умолчанию используетсяvalueавтоматически присваивается этомуkey. Чтобы обеспечить эту функциональность, тип Pythondefaultdictвыполняет две функции:
- Это переопределяет
.__missing__().- Он добавляет
.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, поэтому не забывайте вызывать его, используя круглые скобки, во время инициализации. Это может быть распространенной проблемой, когда вы начинаете использовать тип Pythondefaultdict. Взгляните на следующий код:>>> # 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. Вы можете выполнить этот процесс вручную или автоматизировать его с помощью Pythondefaultdict. В этом разделе вы узнаете, как использовать тип Pythondefaultdictдля решения некоторых распространенных задач программирования:
- Группировка элементов в коллекции
- Подсчет предметов в коллекции
- Накопление значений в коллекции
Вы приведете несколько примеров, в которых используются
list,set,int, иfloatдля выполнения операций группировки, подсчета и накопления в пользовательском-удобный и эффективный способ.Группировка элементов
Типичным использованием типа Python
defaultdictявляется установка для.default_factoryзначенияlist, а затем создание словаря, который сопоставляет ключи со списками значений. С помощью этогоdefaultdict, если вы попытаетесь получить доступ к любому отсутствующему ключу, словарь выполнит следующие действия:
- Вызовите
list()для создания нового пустогоlist- Вставьте пустой
listв словарь, используя отсутствующий ключ в качествеkey- Возвращает ссылку на это
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 + 1var -= 1эквивалентноvar = var - 1var *= 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. Если вы хотите узнать методы и атрибуты, характерные для типа Pythondefaultdict, то вы можете запустить следующую строку кода:>>> set(dir(defaultdict)) - set(dir(dict)) {'__copy__', 'default_factory', '__missing__'}предварительно> кодовый блок>В приведенном выше коде вы используете
dir(), чтобы получить список допустимых атрибутов дляdictиdefaultdict. Затем вы используетеsetdifference, чтобы получить набор методов и атрибутов, которые вы можете найти только вdefaultdict. Как вы можете видеть, различия между этими двумя классами заключаются в следующем. У вас есть два метода и один атрибут экземпляра. В следующей таблице показано, для чего предназначены методы и атрибут:
Method or Attribute Description .__copy__()Provides support for copy.copy().default_factoryHolds the callable invoked by .__missing__()to automatically provide default values for missing keys.__missing__(key)Gets called when .__getitem__()can’t findkeyВ таблице выше вы можете увидеть методы и атрибуты, которые отличают
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. Вот три вещи, которые следует учитывать:
Если ваш код в значительной степени основан на словарях и вы постоянно сталкиваетесь с отсутствующими ключами, то вам следует рассмотреть возможность использования
defaultdictвместо обычногоdict.Если элементы вашего словаря необходимо инициализировать постоянным значением по умолчанию, то вам следует рассмотреть возможность использования
defaultdictвместоdict.Если ваш код использует словари для агрегирования, накопления, подсчета или группировки значений, а производительность вызывает беспокойство, то вам следует рассмотреть возможность использования
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_factoryNone. Если это так, то вы приводите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, который будет содержать вызываемый параметр для генерации значений по умолчанию по требованию. Вот фрагмент кода, который эмулирует большую часть поведения типа Pythondefaultdict: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_factoryNone.В строке 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, которые могут служить для этой цели:С помощью этих двух инструментов вы можете добавить дополнительную гибкость типу 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(). При попытке получить доступ к отсутствующему ключу выполняются следующие действия:
- словарь
def_dictназывает ее.default_factory, которое содержит ссылку наlambdaфункция.- Вызывается функция
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в Pythondefaultdict. Вот пример:>>> 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, и его основная дополнительная функциональность заключается в предоставлении значений по умолчанию для отсутствующих ключей. В этом руководстве вы узнали, как использовать тип Pythondefaultdictдля обработки отсутствующих ключей в словаре.Теперь вы можете:
- Создайте и используйте Python
defaultdictдля обработки недостающих ключей- Решать реальные задачи, связанные с группировкой, подсчетом и накоплением операций
- Знать различия в реализации между
defaultdictиdict- Решите, когда и почему использовать Python
defaultdict, а не стандартныйdictТип 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 для обработки отсутствующих ключей"> кнопка поделиться>defaultdict- это удобная и эффективная структура данных, которая предназначена для того, чтобы помочь вам, когда вы имеете дело с отсутствующими ключами в словаре. Попробуйте и сделайте свой код более быстрым, удобочитаемым и основанным на Python!Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Посмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Обработка пропущенных ключей с помощью типа Python defaultdict
Back to Top