Как использовать DefaultDict в Python

Перевод статьи «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}

Достаточно просто. Эта программа:

  1. Перебирает строку в цикле.
  2. На каждой итерации проверяет, есть ли очередная буква в нашем словаре letters.
  3. Если буква присутствует, к текущему значению ключа добавляется единица.
  4. Если буквы нет в словаре 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.

Этот пример выглядит немного иначе, но идея все та же. Алгоритм действий следующий:

  1. Создаем defaultdict с аргументом default_factory list.
  2. Перебираем в цикле слово «Mississippi».
  3. Если итерируемая буква равна «i», обращаемся к словарю по ключу «i».
  4. Если такой записи в словаре еще не существует, контейнер данных defaultdict создаст ее и использует в качестве значения пустой список.
  5. Затем с помощью спискового метода 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.

Надеюсь, эта статья была вам полезна!

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх