Множества

В математике есть такое понятие как множество (или теория множеств). Возможно вы их даже изучали в курсе математики. Может быть, вы даже знакомы с диаграммами Венна.
На практике множество можно рассматривать просто как четко определенный набор отдельных объектов, которые называются элементами или членами.
Группировка объектов в множество может быть полезна в программировании и Python представляет нам для этого встроенный тип set.

Множества (тип set) отличаются от других типов объектов уникальными операциями, которые можно с ними выполнять.

Встроенный тип set в Python имеет следующие характеристики:
  • элементы множества неупорядочены (это значит, что два множества эквивалентны, если содержат одинаковые элементы). Хранятся элементы множества не последовательно, а по определенным алгоритмам, которые позволяют быстро определять принадлежность элемента множеству (без перебора всех элементов)
  • элементы множества уникальны. Повторяющиеся элементы не допускаются.
  • множества изменяемы (например, можно добавить элемент в множество), но сами элементы внутри множества должны быть неизменяемые (числа, строки, кортежи). Вы не можете сделать элементом множества список или другое множество. 


Создание множества

1 способ - просто перечислить в фигурных скобках элементы, входящие во множество
x = {"school", "teacher", "class", student}

2 способ - использовать встроенную функцию set()
x = set()    # пустое множество
list_name = ["Andry", "Bob", "Caroline"]
y = set(list_name)    # можно создать множество 
                      # из любого итерируемого объекта
z = set(["Andry", "Bob", "Caroline"])     # {'Bob', 'Caroline', 'Andry'}
k = set(("Andry", "Bob", "Caroline"))     # {'Bob', 'Caroline', 'Andry'}
s = "string s"
m = set(s)    # {'i', 't', 'g', 'r', 'n', 's', ' '} - 
              # обратите внимение 
              # порядок элементов может быть любой,
              # элементы не повторяются
n = {42, 'foo', 3.14159, None, (1, 2, 3)}    # элементы в наборе  
                                             # могут быть разного типа
 

Вывод множества

Выводятся элементы множества в произвольном порядке, не обязательно в том, в котором они добавляются.
z = set(["Andry", "Bob", "Caroline"]) 
print(z)     # {'Bob', 'Caroline', 'Andry'}
print(*z)    # Bob Andry Caroline

Количество элементов в множестве

Метод len() возвращает количество элементов в множестве
k = {42, 'foo', 3.14159, None, (1, 2, 3)} 
print(len(k))    # 5


Определение есть ли элемент в множестве (in)

k = {42, 'foo', 3.14159, None, (1, 2, 3)} 
print(42 in k)    # True
print(2 in k)     # False


Хотя элементы, содержащиеся во множестве должны быть неизменяемого типа, сами множества могут быть изменены. 


Добавление элемента к множеству

x.add(<elem>)
к множеству  x добавляет <elem>, который должен быть единственным неизменяемым объектом


Удаление элемента из множества

1) x.remove(<elem>)
удаляется <elem> из множества x. Python вызывает исключение (ошибка), если <elem> нет в x

2) x.discard(<elem>)
то же удаляет, но в случае отсутствия элемента в множестве не вызывает исключения.

3) x.pop()
удаляет и возвращает случайный элемент из множества. Если изначально множество пустое, то возникает исключение (ошибка).

4) x.clear()
удаляет все элементы из множества (очищает множество).

Многие операции, которые можно использовать для других составных типов (например, списки, кортежи), не имеют смысла для множества.
Например, обращение по индексу и срезы. Однако Python позволяет выполнять ряд операций с элементами множества, которые имитируют операции с математическими множествами.
Большинство из этих операций можно выполнять двумя способами: операцией или определенным методом.


ОБЪЕДИНЕНИЕ МНОЖЕСТВ

x1 = {'foo', 'bar', 'baz'}
x2 = {'baz', 'qux', 'quux'}
print(x1 | x2)         # {'bar', 'baz', 'qux', 'quux', 'foo'}
print(x1.union(x2))    # {'bar', 'baz', 'qux', 'quux', 'foo'}

Операция объединения множеств (|) и аналогичный метод имеют тонкая разницу. Когда вы используете | оператор, должны быть установлены оба операнда. Метод union() примет любой итеративный элемент в качестве аргумента, преобразует его во множество и затем выполнит объединение.

Обратите внимание на разницу
x1 = {'foo', 'bar', 'baz'print(x1.union(('baz', 'qux', 'quux')))    # {'baz', 'bar', 'quux', 'foo', 'qux'}
print(x1 | ('baz', 'qux', 'quux'))         # builtins.TypeError: 
                                           # unsupported operand type(s) for |: 'set' and 'tuple'



ОПЕРАЦИИ И МЕТОДЫ ДЛЯ МНОЖЕСТВ

Ниже приведен список операций, доступных в Python. Некоторые выполняются оператором, некоторые - методом, а некоторые - обоими. Принцип, изложенный выше, обычно применяется: там, где ожидается множество, методы обычно принимают любой итеративный элемент в качестве аргумента, но операторы требуют фактических множеств в качестве операндов.
Пояснение: записи в квадратных скобках указывают на то, что эта часть может быть опущена
 
Название Метод Оператор Описание
объединение x1.union(x2[, x3, …]) x1 | x2 [| x3 …] возвращает множество всех элементов, которые присутствую либо в x1 либо в x2 (либо других)
пересечение множеств x1.intersection(x2[, x3, …]) x1 & x2 [& x3 …] возвращает множество элементов, общих для x1 и x2 (и других)
разница между множествами x1.difference(x2) x1 - x2 [- x3 …] возвращает множество элементов, которые есть в x1, но нет в x2 (только -  поддерживает множественность)
симметричная разница между множествами x1.symmetric_difference(x2) x1 ^ x2 [^ x3] возвращает множество элементов, которые есть в x1 или x2, но не в обоих сразу (только ^ поддерживает множественность)
общие элементы в наборах x1.isdisjoint(x2) нет возвращает True, если x1 и x2 не имеют общих элементов
x1 подмножество x2 x1.issubset(x2) x1 <= x2 True, если x1 подмножество x2, иначе - False
Правильное подмножество - x1 < x2 True, если x1 является правильным подмножеством x2
x1 надмножество x2 x1.issuperset(x2) x1 >= x2 True, если x1 содержит все элементы x2
Правильное надмножествво - x1 > x2 True, если x1 является правильным надмножеством x2
Объединение множеств x1.update(x2[, x3 ...]) x1 |= x2 [| x3 ...] Добавляет к x1 элементы x2, которых нет в x1
Пересечение множеств x1.intersection_update(x2[, x3 ...]) x1 &= x2 [& x3 ...] Обновляет x1, сохраняя элементы, которые есть в обоих множествах x1 и x2
Разница множеств x1.difference_update(x2[, x3 ...]) x1 -= x2 [| x3 ...] Обновляет x1, удаляя элементы, найденные в x2
Симметричная разность x1.symmetric_difference_update(x2) x1 ^= x2 Обновляет x1, сохраняя элементы, найденные в x1 ли x2, но не в обоих

Как мы знаем порядок следования элементов в множестве не учитывается. Поэтому есть ли смысл говорить о сортировке множеств в Python 3.

Но для быстрого поиска элемента во множестве, желательно их хранить в памяти в упорядоченном виде.

Рассмотрим несколько примеров. Что будет с элементами разных типов данных в одном множестве? Такие элементы не должны сортироваться. Если мы будем выводить элементы с помощью команды print, то они выводятся примерно следующим образом:

a = {0, 1, 12, 'b', 'ab', 3, 2, 'a'}
print(a)    # {0, 1, 2, 3, 'a', 12, 'b', 'ab'}

У нас вывелись не отсортированные значения, если повторить запуск, то порядок будет может быть другой. Но это только в том случае, если перемешаны элементы разного типа.

Давайте попытаемся вывести только числа:
a = {0, 1, 12, 3, 2}
print(a)    # {0, 1, 2, 3, 12}

Все элементы выведены упорядоченно. Попробуем преобразовать в список:
a = {0, 1, 12, 3, 2}
b = list(a)
print(b)    # [0, 1, 2, 3, 12]
 

Аналогично, в список значения записались отсортированными по возрастанию.

Получается, что элементы хранятся в памяти в упорядоченном виде, если они одного типа. Но лучше не стоит на это расчитывать, алгоритмы Python могут поменяться.

Если вам нужно получить отсортированный список из множества, лучше воспользоваться для верности функцией sort (sorted). Элементы будут точно отсортированы. Ваш код будет понятен для других.