Перевод статьи «How to Use DefaultDict in Python».
На протяжении всего времени работы с наборами данных в Python словарь был моей самой используемой структурой данных. Он универсален и прост в использовании.
Нужно подсчитать количество вхождений символа? Используйте словарь!
Хотите создать список футболистов и связанных с ними статистических данных? Словарь!
Однако они не являются безотказными. Во многих задачах при работе с данными вы сталкиваетесь с большим количеством ошибок KeyErrors, и это может раздражать.
Устранение этих ошибок приводит к появлению нескольких дополнительных строк кода. Это снижает читабельность и повышает сложность. Если вы работаете с большим количеством данных, эта проблема может выйти из-под контроля.
Модуль collections решает эту проблему сложности. Модуль collections — это часть стандартной библиотеки Python, которая содержит несколько замечательных способов работы с данными. Основная цель модуля — сделать ваш код более читабельным и упростить обработку данных с помощью некоторых дополнительных типов.
Я чаще всего использую defaultdict, и сегодня мы рассмотрим несколько простых примеров его применения. Чтобы в полной мере оценить этот контейнер данных, вы должны обладать рабочими знаниями о Python. Точнее, быть знакомым с обычными словарями.
Как упростить код с помощью DefaultDict
Прежде чем мы перейдем к сегодняшней теме, давайте рассмотрим ситуацию. Я хочу создать словарь, который выдавал бы мне количество уникальных букв в слове «Mississippi». Там много букв «s» и «p», и у меня нет времени пересчитывать их вручную.
Вот как я могу это сделать, используя стандартный словарь:
letters = {} for letter in "Mississippi": if letter not in letters: letters[letter] = 1 else: letters[letter] +=1 print(letters) # {'M': 1, 'i': 4, 's': 4, 'p': 2}
Достаточно просто. Эта программа:
- Перебирает строку в цикле.
- На каждой итерации проверяет, есть ли очередная буква в нашем словаре letters.
- Если буква присутствует, к текущему значению ключа добавляется единица.
- Если буквы нет в словаре letters, программа добавляет ее в словарь в качестве ключа и устанавливает начальное значение в 1.
Этот пример был довольно простым, но вы уже видите, как усложняется код. Давайте посмотрим, как можно сделать лучше:
from collections import defaultdict letters = defaultdict(int) for letter in "Mississippi": letters[letter] += 1 print(letters) # defaultdict(<class 'int'>, {'M': 1, 'i': 4, 's': 4, 'p': 2})
Как видите, все условные операторы теперь исчезли. Код стало читать немного легче, но в конце программы мы все равно получили тот же результат.
В этом и заключается преимущество defaultdict. Давайте разберем этот контейнер данных подробнее.
Изучение контейнера данных DefaultDict
Идея defaultdict проста: если мы пытаемся получить доступ к значению несуществующего ключа, в словарь добавляется пара ключ-значение с этим ключом и значением, заданным по умолчанию.
В приведенном выше примере мы начали с пустого defaultdict без записей. Для каждой уникальной буквы словарь создал запись. Поскольку в качестве значения по умолчанию мы использовали int
, значение созданной записи было равно 0. После создания записи словарь добавил к этому значению единицу.
В конце программы выводится количество букв, причем нам не приходится использовать условия или как-то вмешиваться вручную. Очень питонично.
Как установить значение по умолчанию в DefaultDict
Контейнер данных defaultdict при инициализации принимает один аргумент с именем default_factory
.
Этот аргумент default_factory
представляет собой функцию. Когда программа пытается получить доступ к несуществующей записи, defaultdict вызывает default_factory
без каких-либо аргументов. Так, например, я могу вызвать defaultdict с функцией int()
следующим образом:
d1 = defaultdict(int)
Когда я попытаюсь получить доступ к несуществующей записи, функция добавит к этой записи значение функции int
, равное 0.
d1 = defaultdict(int) d1[“Adding an entry!”] Print(d1) # defaultdict(<class 'int'>, {'Adding an Entry!': 0})
Изучение возможностей DefaultDict
Теперь, когда вы знаете основные принципы использования defaultdict, мы можем изучить его возможности.
Как я уже говорил, default_factory
— это функция без аргументов. Это означает, что мы можем использовать как встроенные типы данных, так и пользовательские функции — при условии, что они не принимают аргументов.
Давайте вернемся к нашему примеру с Mississippi. Я хочу узнать все индексы, под которыми стоят буквы «i». Я собираюсь использовать defaultdict со списком в качестве аргумента default_factory
, чтобы мы могли отслеживать все индексы.
from collections import defaultdict my_word = "Mississippi" d1 = defaultdict(list) for index, letter in enumerate(my_word): if letter == "i": d1[letter].append(index) print(d1) # defaultdict(<class 'list'>, {'i': [1, 4, 7, 10]})
Потрясающе! Я проверил этот пример вручную, и, похоже, он правильный. Буква i находится под индексами 1, 4, 7 и 10.
Этот пример выглядит немного иначе, но идея все та же. Алгоритм действий следующий:
- Создаем defaultdict с аргументом
default_factory
list
. - Перебираем в цикле слово «Mississippi».
- Если итерируемая буква равна «i», обращаемся к словарю по ключу «i».
- Если такой записи в словаре еще не существует, контейнер данных defaultdict создаст ее и использует в качестве значения пустой список.
- Затем с помощью спискового метода
append
добавляем индекс итерируемой буквы.
Давайте изучим этот вопрос подробнее. Поскольку default_factory
принимает функцию в качестве аргумента, мы можем определить свою собственную — при условии, что наша пользовательская функция не принимает аргумент.
from collections import defaultdict def return_hello(): return "Hello!" d1 = defaultdict(return_hello) d1[1] d1[2] d1[3] print(d1) # defaultdict(<function return_hello at 0x0000014FC5D28DC0>, {1: 'Hello!', 2: 'Hello!', 3: 'Hello!'})
Здесь я определил функцию, которая просто возвращает «Hello!», и передал ее в качестве аргумента default_factory
. Теперь, когда мы пытаемся получить доступ к несуществующим записям в нашем словаре, defaultdict вызывает мою пользовательскую функцию, чтобы определить значение по умолчанию!
В заключение
В этом руководстве мы рассмотрели defaultdict, который является контейнером данных во встроенном модуле collections из стандартной библиотеки Python. Он позволяет нам получить доступ к несуществующим записям в словаре, создавая их на лету и присваивая значение по умолчанию.
defaultdict принимает аргумент default_factory
, указывающий словарю значение по умолчанию, которое следует присвоить ключу. В качестве аргумента могут использоваться встроенные функции, такие как int
или list
, или пользовательские функции, такие как наша функция return_hello
.
Надеюсь, эта статья была вам полезна!