Множества – самая сложная для понимания структура данных, если у вас нет высшего математического образования. Освоить базовые операции со множествами – просто, но как только вы познакомитесь с математическими операциями, вас будет преследовать простой, но навязчивый вопрос: «А зачем это вообще нужно?». Ниже мы на этот вопрос постараемся ответить, пока будем рассказывать про все функции множеств.
В математике множество – это набор любых уникальных объектов. Множества в Python – это тип данных, который позволяет собирать математические множества. Свойства множеств в Python:
Последнее связано с тем, что в оперативной памяти множество превращается в хэш-число – уникальную последовательность данных. Преобразование множества в хэш имеет 2 важных последствия:
Само по себе множество – это просто забавная структура данных со своими особенностями. Истинная мощь множеств раскрывается, когда нам нужно произвести какую-либо операцию над двумя множествами. Например: ваше приложение-маркетплейс собирает данные о предпочтениях пользователей для того, чтобы впоследствии решить, на какие товары выдавать скидки. Все данные обо всех пользователях хранятся в базе данных. Пришла пора эти данные проанализировать, и перед вами стоит задача: для каждого пользователя в базе нужно взять все категории, в которых пользователь делал покупки, и найти самую частую (популярную). Данные в базе хранятся не очень продуманно – номера категорий, которыми интересовался пользователь, записаны в string через запятую.
Кто и зачем придумал такую неудобную базу – другой вопрос, вам сейчас нужно решить практическую задачу на основе имеющихся инструментов. Распарсить строку конкретного пользователя – не проблема, но что дальше? Вы можете передавать результат парсинга в функцию, которая будет подсчитывать упоминания каждой категории и хранить счетчики в отдельном списке, но:
Вот как эту задачу можно решить с помощью множеств – при условии, что «db_result» – это итератор, который последовательно подкидывает результат пользователя из базы данных:
result = set()
for item in db_result:
result |= set(item.split(‘,’))
И работать это будет в 100 раз быстрее – весь поиск займет около 50 минут, а не 80 часов.
Если вам нужно создать пустое множество – используйте встроенную функцию языка set или frozenset:
test_set = set()
test_frozenset = frozenset()
«Знак» множества – фигурные скобки, как вы увидите в дальнейшем. Такой же знак – у словарей (у разработчиков закончились скобки для обозначения структур данных). Когда вы пользуетесь фигурными скобками для создания непустой структуры – у интерпретатора не возникает вопросов, потому что во множествах хранятся одиночные данные, а в словарях хранятся парные. А вот в случае с «{}» понять, какая именно структура данных здесь подразумевалась, не получается, поэтому разработчики решили, что «{}» – это словарь, а для множеств нужно использовать set().
Самый простой способ создать непустое множество – просто перечислить данные в скобках:
test_set = {1, 4, 2, 5, 3}
В большинстве случаев множество нужно создать на основе какой-то другой итерируемой структуры данных – в этом случае структуру данных нужно передать в функцию set() или frozenset(), и язык все сделает за вас:
Как и в случае с другими функциями, создающими структуры данных – если вы передадите в set() словарь, из него возьмутся только ключи. Чтобы передать во множество пары «ключ:значение» в кортежах, вам нужно использовать встроенный метод словарей .items():
Наконец, последний способ создания множеств – генераторы (set comprehension). Python позволяет описать содержимое множества через короткую запись for:
{действие for переменная in последовательность if условие}
Писать условие не обязательно. Например, мы хотим создать множество кубов для всех чисел от 1 до 100, если число делится на 3 без остатка:
Поскольку данные во множествах хранятся в произвольном порядке – мы не можем получить к ним доступ по индексу. Остается 2 инструмента: in и pop(). In работает как для set, так и для frozenset – он позволяет проверить наличие элемента во множестве, возвращает True или False:
pop() – это встроенный метод множества, который удаляет случайное значение и возвращает его. Этот метод может быть полезен, если мы, например, пишем рандомизатор для поиска победителя в конкурсе. Метод работает только с set(); прочитать конкретное значение можно единожды, потому что оно удаляется из множества:
Если нам нужно добавить один элемент – пользуемся встроенным методом add(), если нужно добавить несколько элементов – пользуемся встроенным методом update(), которому передаем итерируемый объект:
С frozenset все это не работает. Еще один способ добавления элементов – через объединение множеств, но про это мы расскажем позже.
Все методы этого подраздела работают только с set. Про метод pop() мы уже рассказывали выше – он удаляет случайное значение и возвращает его. Есть еще 3 метода для удаления данных из множества:
К слову, проверить наличие элементов во множестве можно либо через bool(), либо через len() – нужно передать множество этим функциям, bool() при пустом множестве вернет False, len() вернет 0.
Множество перебирается так же, как и другие итерируемые объекты – через for:
Как мы уже отмечали выше – множества не являются упорядоченными, поэтому не стоит писать алгоритмы, полагающиеся на последовательность добавленных элементов.
Если нужно отсортировать множество – пользуйтесь встроенной в язык функцией sorted:
Обратите внимание – результатом сортировки множества является список, что логично, поскольку ввиду неупорядоченности множество не может быть отсортированным по определению.
Вам будет намного проще понимать математические операции над множествами, если сначала вы узнаете, зачем они нужны. Все математические операции над множествами сводятся к выявлению между ними отношений: какие элементы присутствуют в обоих множествах, какие есть только в одном, входят ли все элементы одного множества в другое (является ли одно множество подмножеством другого) и так далее. Все эти операции оказываются очень удобными, когда нам нужно выяснить отношения между двумя массивами данных – например, если мы ищем самую популярную категорию товаров как в примере выше.
Математические операции, которые можно совершать над множествами: объединение, пересечение, разность и симметрическая разность, результатом любой операции будет пустое или непустое множество. Кроме того, можно выяснить, является ли одно множество подмножеством другого – или же они абсолютно равны, то есть в обоих присутствуют одинаковые элементы. Все эти операции и отношения крайне удобно показывать на диаграммах Венна – диаграммах пересекающихся кругов, ниже мы будем использовать их для наглядной иллюстрации.
Результатом объединения двух множеств является объединенное множество – в нем есть элементы и из первого, и из второго множеств:
Есть 2 формы записи для объединения:
Для методов – в качестве аргумента можно передать любой iterable, аргументов может быть несколько. Для короткой записи – операндами должны быть конкретно множества.
Результатом пересечения двух множеств является множество, в котором есть элементы, которые встретились и в первом, и во втором множествах:
Есть 2 формы записи для объединения:
Для методов – в качестве аргумента можно передать любой iterable, аргументов может быть несколько. Для короткой записи – операндами должны быть конкретно множества.
Результатом разности множества А и множества Б являются те элементы, которые присутствуют в А, но не присутствуют в Б:
Есть 2 формы записи для разности:
Для методов – в качестве аргумента можно передать любой iterable, аргументов может быть несколько. Для короткой записи – операндами должны быть конкретно множества.
Результатом симметрической разности двух множеств является множество, в котором есть элементы, которые встретились либо только в первом, либо только во втором множестве:
Есть 2 формы записи для объединения:
Для методов – в качестве аргумента можно передать любой iterable, аргументов может быть несколько. Для короткой записи – операндами должны быть конкретно множества.
Если у нас есть множество A, то подмножество – это такое множество B, все элементы которого есть в A:
Если у нас есть множество А, то надмножество – это такое множество B, которое включает в себя все элементы, присутствующие в A:
Если два множества содержат одни и те же элементы – они равны между собой. Строгое подмножество (надмножество) – это когда одно из множеств точно больше другого; нестрогое подмножество (надмножество) – это когда множества могут быть равны.
Наконец, последнее из возможных отношений – два множества, которые вообще не совпадают по своим элементам, они называются непересекающимися.
Теперь покажем, как все эти отношения находятся методами и знаками сравнения:
Методы принимают любой iterable объект, короткая же запись требует, чтобы обоими операндами были множества.
По пунктам: