Collections — это встроенный модуль Python, предоставляющий такие полезные типы данных, как контейнеры.
Контейнерные типы данных позволяют нам хранить и получать доступ к значениям удобным способом. Как правило, мы используем списки, кортежи и словари. Но при работе со структурированными данными нам нужны более умные объекты.
Сегодня мы разберем различные структуры данных, поддерживаемые модулем collections, и на примерах рассмотрим, когда их стоит использовать.
Итак, давайте приступать! Однако сначала нам обязательно нужно импортировать collections :
Именованный кортеж namedtuple
Что такое именованный кортеж?
namedtuple можно представить как расширенную версию обычного кортежа tuple либо как быстрый способ создать класс с определенными именованными атрибутами.
Ключевое различие между кортежем и именованным кортежем заключается в следующем. Кортеж позволяет вам обращаться к данным через индексы, а с именованным кортежем вы можете получить доступ к элементам непосредственно по их именам.
Фактически вы можете определить, какие атрибуты может содержать именованный кортеж, и создать несколько его экземпляров. Точно так же, как вы поступили бы с классами.
Таким образом, с точки зрения функциональности он больше похож на класс, хотя и называется кортежем.
Давайте создадим namedtuple, который представляет из себя фильм movie с такими атрибутами, как жанр (genre), рейтинг (rating) и ведущий актер (lead_actor).
Теперь мы можем получить доступ к любой информации о нашем фильме, используя идентификатор. Это довольно быстро и удобно.
Альтернативный способ создания namedtuple
В качестве альтернативы вы можете передать список, состоящий из имен полей, вместо просто имен полей, разделенных пробелом.
К примеру, это может выглядеть так:
Доступ к элементам в namedtuple возможен как по индексу, так и по идентификатору.
Зачем использовать namedtuple вместо обычного словаря
Основным преимуществом namedtuple является то, что он занимает меньше места (памяти), чем аналогичный словарь.
Поэтому, в случае больших данных именованные кортежи эффективны.
Пример:
Выполняя приведенный выше код, вы обнаружите, что namedtuple имеет размер 64 байта, тогда как словарь занимает гораздо больше — 240 байт. Это почти в 4 раза больше памяти.
А теперь представьте себе эффект при обработке большого количества таких объектов. В общем, с целью экономии памяти гораздо лучше использовать именованные кортежи.
Как создать namedtuple из словаря в Python
Вы заметили, как мы преобразовали словарь в именованный кортеж с помощью оператора **?
Все, что вам нужно сделать для этого — определить структуру namedtuple и передать словарь (**dict) этому именованному кортежу в качестве аргумента. Единственное требование заключается в том, что ключи словаря должны совпадать с именами полей namedtuple.
Как заменить атрибут в именованном кортеже
Что делать, если значение одного атрибута необходимо изменить?
Вам нужно обновить его в данных. Для этого просто воспользуемся методом ._replace()
Counter
Объект counter предоставляется библиотекой collections. Давайте поподробнее разберем, что он собой представляет.
К примеру, у вас есть список каких-то случайных чисел. Что, если вы хотите узнать, сколько раз встречается каждое число?
Счетчик counter позволяет легко вычислить частоту. Он работает не только с числами, но и с любым итерируемыми объектами, такими как строки и списки.
Counter — это подкласс dict, используемый для подсчета хешируемых объектов.
Он возвращает словарь с элементами в качестве ключей и “счетчиком” (количество вхождений элемента) в качестве значений.
Примеры
Теперь давайте разберем некоторые примеры.
Давайте используем счетчик counter, чтобы найти частоту вхождений каждого символа в строке.
Как мы видим, counter позволяет посмотреть, какие элементы есть в строке и сколько их.
Усложним задачу. Допустим, у вас есть предложение и вы хотите посмотреть количество слов в нем. Как это сделать?
При помощи функции split() можно составить список слов в предложении и передать его в Counter().
Как найти наиболее частотные элементы с помощью счетчика
Счетчик очень полезен в реальных приложениях. Особенно, когда вам нужно обработать большие данные, и вы хотите узнать частотность некоторых элементов. Давайте рассмотрим несколько очень полезных методов, использующих counter.
Counter().most_common([n])
Такое выражение возвращает список «n наиболее распространенных элементов» вместе с их количеством в порядке убывания.
Метод most_common() можно использовать для вывода наиболее часто повторяющегося элемента. Используется в частотном анализе.
Этот же метод можно использовать для поиска наиболее частотного символа в строке.
Что произойдет, если вы не укажете n при использовании most_common(n)?
Тогда все элементы с их количеством вхождений будут напечатаны в порядке убывания количества.
Еще есть метод Counter().elements(), который возвращает все элементы, количество которых больше 0.
Defaultdict
Словарь представляет из себя неупорядоченный набор ключей и значений.
В парах ключ:значение ключи должны быть уникальны и неизменяемы. Поэтому список не может быть ключом словаря, так как он изменяемый. А вот кортеж может.
Чем defaultdict отличается от простого словаря?
Если вы попытаетесь получить доступ к ключу, которого нет в словаре, он выдаст ошибку KeyError. В то время как при использовании defaultdict такой ошибки не будет.
Если вы попробуете обратиться к отсутствующему ключу, defaultdict просто вернет значение по умолчанию.
Синтаксис будет следующим: defaultdict(default_factory).
При обращении к отсутствующему ключу функция default_factory вернет значение по умолчанию.
При запуске этого кода вы не получите KeyError.
Если вы хотите вывести сообщение о том, что значение запрошенного ключа отсутствует, можно определить собственную функцию и передать ее в defaultdict.
К примеру, это может выглядеть так:
Во всем остальном defaultdict ничем не отличается от обычного словаря. Синтаксис команд полностью идентичен.
Вы также можете обойти ошибку KeyError и при использовании обычного словаря — с помощью метода get().
OrderedDict
Словарь — это неупорядоченная коллекция пар ключ-значение. Однако OrderedDict поддерживает упорядочивание ключей.
Это в некотором роде подкласс словаря dict.
Давайте создадим обычный словарь и сделаем его OrderedDict, чтобы показать, в чем заключается разница.
В OrderedDict даже после изменения значения некоторых ключей порядок остается прежним.
При повторной вставке ключа запись считается новой.
Вы видите, что bicycle находится в конце, так как порядок изменился, когда мы удалили ключ.
Есть несколько полезных команд, которые можно использовать с OrderedDict. К примеру, мы можем выполнять функции сортировки.
Сортировка с помощью OrderedDict
Сортировка элементов, например, по возрастанию значений, может помочь в анализе данных. Давайте посмотрим, что мы можем сделать.
Сортировка элементов по ключу KEY (в порядке возрастания):
Сортировка пар по значению VALUE (в порядке возрастания):
Сортировка словаря по длине строки ключа (в порядке возрастания):
ChainMap
ChainMap — это контейнерный тип данных, в котором хранится несколько словарей.
Если у вас несколько связанных или похожих словарей, зачастую их можно хранить вместе, в ChainMap.
Распечатать все элементы ChainMap можно при помощи .map:
Также мы можем вывести ключи всех словарей, используя функцию .keys():
Можно получить и значения всех словарей в ChainMap — при помощи функции .values().
Что происходит, когда у нас есть избыточные ключи в ChainMap?
Возможно, что 2 словаря содержат один и тот же ключ. К примеру, как это показано ниже:
Обратите внимание, что red не повторяется, он печатается только один раз.
Как добавить новый словарь в ChainMap?
Вы можете добавить новый словарь в начало ChainMap, используя метод .new_child().
Как изменить порядок словарей в ChainMap?
Порядок, в котором словари хранятся в ChainMap, можно изменить с помощью функции reversed().
UserList
UserList — это похожий на список контейнерный тип данных, который является классом-оболочкой для списков.
Синтаксис будет следующим: collections.UserList([list]).
Вы передаете обычный список в качестве аргумента userlist. Этот список хранится в атрибуте ‘data’ и доступен через метод UserList.data.
В чем польза UserLists?
Предположим, вы хотите удвоить все элементы в определенных списках. Или, может быть, вы хотите убедиться, что ни один элемент не может быть удален из заданного списка.
В таких случаях нам нужно добавить в наши списки определенное «поведение», что можно сделать с помощью UserLists.
Давайте рассмотрим, как можно использовать UserList для переопределения функциональности встроенного метода. Приведенный ниже код предотвращает добавление нового значения в список:
Код выводит сообщение RunTimeError и не позволяет добавить элемент в список. Это может быть полезно, если вы хотите не допустить внесения информации после определенного срока.
UserString
Подобно тому, как UserLists является классом-оболочкой для списков, UserString является классом-оболочкой для строк.
UserString позволяет добавлять к строке определенное поведение. Вы можете передать этому классу любой конвертируемый в строку аргумент и затем получить доступ к этой строке, используя атрибут data.
Как видите, число 765 было преобразовано в строку «765», и доступ к ней можно получить с помощью метода UserString.data.
Как и когда можно использовать UserString
UserString можно использовать для изменения строк или выполнения определенных функций.
Предположим, вы хотите удалить определенное слово из текстового файла (где бы оно ни было). Возможно, некоторые слова в тексте неуместны.
Давайте посмотрим на пример того, как UserString можно использовать для удаления определенных слов из строки.
В этом примере pencil и eraser были удалены с помощью функционального класса user_string.
Рассмотрим другой случай. Что делать, если вам нужно заменить слово другим во всем файле?
При помощи UserString сделать это просто. Мы определили функцию внутри класса, чтобы заменить определенное слово на The Chairman во всем тексте.
Как видите, слово Rajesh везде заменено на The Chairman.
UserDict
Это класс-оболочка для словарей. Его синтаксис аналогичен UserList и UserString.
Мы передаем словарь в качестве аргумента, который хранится в атрибуте ‘data’.
Как можно использовать UserDict
UserDict позволяет нам создать словарь и модифицировать его под наши нужды. Давайте посмотрим на пример того, как UserDict можно использовать для переопределения функциональности встроенного метода. Приведенный ниже код предотвращает удаление пары ключ-значение.
Вы получите сообщение RunTimeError. Это может помочь, если вы не хотите потерять данные.
Что делать, если некоторые ключи имеют ненужные значения, и вам нужно заменить их на null или 0? Эту проблему можно решить следующим образом:
Поле с ненужными значениями заменено на 0. И это лишь простые примеры того, как UserDict позволяет создать словарь с необходимой функциональностью.
Заключение
Итак, мы разобрали основные типы контейнерных данных с примерами. Они значительно повышают эффективность работы при использовании на больших наборах данных.
Надеемся, данная статья была вам полезна! Успехов в написании кода!