Пропустить до содержимого

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

[

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

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

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

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

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

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

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

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

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

  1. Использование метода .get()
  2. Использование метода .setdefault()
  3. Использование исключения KeyError в комбинации с ловушкой try-except
  4. Использование типа данных defaultdict

В данном руководстве мы сосредоточимся на четвертом способе - типе данных defaultdict.

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

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

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

from collections import defaultdict
def default_factory():
return 'default value'
my_dict = defaultdict(default_factory)

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

Использование типа данных defaultdict в Python

Тип данных defaultdict предоставляет различные возможности для работы с отсутствующими ключами в словарях. Рассмотрим некоторые из них:

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

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

from collections import defaultdict
my_dict = defaultdict(list)
fruits = ['apple', 'banana', 'cherry', 'apple', 'banana']
for fruit in fruits:
my_dict[fruit].append(fruit)
print(my_dict)

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

defaultdict(<class 'list'>, {'apple': ['apple', 'apple'], 'banana': ['banana', 'banana'], 'cherry': ['cherry']})

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

Если вам нужно группировать только уникальные элементы, то можно использовать тип данных defaultdict(set):

from collections import defaultdict
my_dict = defaultdict(set)
fruits = ['apple', 'banana', 'cherry', 'apple', 'banana']
for fruit in fruits:
my_dict[fruit].add(fruit)
print(my_dict)

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

defaultdict(<class 'set'>, {'apple': {'apple'}, 'banana': {'banana'}, 'cherry': {'cherry'}})

Подсчет элементов

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

from collections import defaultdict
my_dict = defaultdict(int)
fruits = ['apple', 'banana', 'cherry', 'apple', 'banana']
for fruit in fruits:
my_dict[fruit] += 1
print(my_dict)

В данном примере, мы создаем словарь my_dict с типом defaultdict(int), чтобы при обращении к отсутствующему ключу создавалось целочисленное значение 0. Затем мы проходим по списку фруктов и увеличиваем значение для каждого фрукта на единицу. Результат будет следующим:

defaultdict(<class 'int'>, {'apple': 2, 'banana': 2, 'cherry': 1})

Накопление значений

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

from collections import defaultdict
my_dict = defaultdict(float)
fruits = ['apple', 'banana', 'cherry', 'apple', 'banana']
prices = [1.5, 2.0, 0.75, 1.2, 1.8]
for fruit, price in zip(fruits, prices):
my_dict[fruit] += price
print(my_dict)

В данном примере, мы создаем словарь my_dict с типом defaultdict(float), чтобы при обращении к отсутствующему ключу создавалось числовое значение 0.0. Затем мы проходим по спискам фруктов и цен и накапливаем значения цен для каждого фрукта. Результат будет следующим:

defaultdict(<class 'float'>, {'apple': 2.7, 'banana': 3.8, 'cherry': 0.75})

Углубление в тип данных defaultdict

Тип defaultdict имеет несколько особенностей и отличий от обычного словаря dict. Рассмотрим некоторые из них:

defaultdict vs dict

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

defaultdict.default_factory

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

from collections import defaultdict
my_dict = defaultdict(str)
print(my_dict.default_factory) # <class 'str'>
my_dict.default_factory = int
print(my_dict.default_factory) # <class 'int'>

В приведенном выше примере, мы создаем словарь my_dict с типом defaultdict(str), то есть значением по умолчанию будет пустая строка. Затем мы получаем доступ к атрибуту default_factory словаря и видим, что тип это <class 'str'>. Затем мы изменяем default_factory на тип int и снова выводим его значение, которое становится <class 'int'>.

defaultdict vs dict.setdefault()

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

from collections import defaultdict
my_dict = defaultdict(int)
my_dict['apple'] = 1
my_dict.setdefault('banana', 0)
print(my_dict)

В данном примере, мы создаем словарь my_dict с типом defaultdict(int). Затем мы устанавливаем значение для ключа 'apple' равным 1. Затем мы используем метод setdefault() для ключа 'banana' с значением по умолчанию 0. Результат будет следующим:

defaultdict(<class 'int'>, {'apple': 1, 'banana': 0})

defaultdict.missing()

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

from collections import defaultdict
class MyDefaultDict(defaultdict):
def __missing__(self, key):
return f'missing key: {key}'
my_dict = MyDefaultDict(int)
my_dict['apple'] = 1
print(my_dict['banana'])

В данном примере, мы создаем наследника от defaultdict - класс MyDefaultDict, и определяем свою собственную реализацию метода __missing__(), который возвращает строку, содержащую отсутствующий ключ. Затем мы создаем словарь my_dict с типом MyDefaultDict(int) и устанавливаем значение для ключа 'apple' равным 1. Затем мы пытаемся получить доступ к отсутствующему ключу 'banana' и выводим результат:

missing key: banana

Эмуляция типа данных defaultdict

Многие из возможностей типа данных defaultdict можно эмулировать с помощью обычного словаря dict и метода .get():

my_dict = {}
my_dict.get('apple', []).append('apple')

В данном примере, мы создаем пустой словарь my_dict и используем метод .get() для получения значения по ключу 'apple'. Если ключа нет в словаре, метод .get() вернет пустой список []. Затем мы применяем метод .append() к списку, что позволяет нам добавлять элементы к списку, даже если ключа не существует.

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

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

Тип данных defaultdict позволяет передать аргументы в функцию default_factory для генерации значений по умолчанию. Рассмотрим два способа передачи аргументов - с использованием лямбда-функции и с использованием functools.partial():

Использование лямбда-функции

from collections import defaultdict
my_dict = defaultdict(lambda: 'default value')
print(my_dict['apple']) # default value
my_dict = defaultdict(lambda x: 'value: ' + x)
print(my_dict['apple']) # value: apple

В приведенном выше примере, мы создаем словарь my_dict с типом defaultdict и передаем лямбда-функцию в качестве значения по умолчанию. В первом случае, все отсутствующие ключи будут иметь значение 'default value'. Во втором случае, значение по умолчанию будет генерироваться с помощью переданной лямбда-функции, которая принимает ключ и возвращает строку 'value: ' + ключ.

Использование functools.partial()

from collections import defaultdict
from functools import partial
def default_factory(x):
return f'value: {x}'
my_dict = defaultdict(partial(default_factory, x='default value'))
print(my_dict['apple']) # value: apple
my_dict = defaultdict(partial(default_factory, x='value'))
print(my_dict['apple']) # value: apple

В приведенном выше примере, мы создаем функцию default_factory, которая принимает аргумент x и возвращает значение по умолчанию. Затем мы создаем словарь my_dict с типом defaultdict и передаем функцию default_factory, частично примененную с помощью functools.partial(), в качестве значения по умолчанию. В результате, все отсутствующие ключи будут иметь значение, сгенерированное функцией default_factory, с переданным аргументом x.

Заключение

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