Множества в Python

Множество (англ. «set«) – неупорядоченная коллекция из уникальных (неповторяющихся) элементов. Элементы множества в Python должны быть немутабельны (неизменяемы), хотя само содержимое множества может меняться: можно добавлять и удалять элементы из множества.

О неизменяемых множествах написано в конце этой статьи.

CPython: внутри множества реализованы как хэш-таблицы, в которых есть только ключи без значений и добавлены некоторые оптимизации, которые используют отсутствие значений. Проверка членства выполняется за время O(1), так как поиск элементов в хэш-таблицы тоже выполняется за О(1). Если интересно, как это реализовано на С: вот ссылка.

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

Сформировать множество можно несколькими способами. Самый простой – перечислить элементы через запятую внутри фигурных скобок {}. Множество может содержать элементы разных типов, главное, чтобы они были неизменяемы. Поэтому кортеж можно поместить в множество, а список – нельзя.

>>> my_set = {1, 2, 3, 4}

>>> my_hetero_set = {"abc", 3.14, (10, 20)}  # можно с кортежем

>>> my_invalid_set = {"abc", 3.14, [10, 20]}  # нельзя со списком
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Можно также воспользоваться встроенной функцией set, чтобы создать множество из другой коллекции: списка, кортежа или словаря. Если это будет словарь – то новое множество будет составлено только из ключей этого словаря. Можно создать множество даже из строки: будет добавлена каждая буква (но только один раз):

>>> my_set2 = set([11, 22, 33])
>>> my_set2
{33, 11, 22}

>>> my_set3 = set((1, 2, 3))
>>> my_set3
{1, 2, 3}

>>> my_set4 = set({"a": 10, "b": 20})
>>> my_set4
{'b', 'a'}

>>> my_set5 = set("hello")
>>> my_set5
{'h', 'l', 'e', 'o'}

Как создать пустое множество? {} – вернет нам пустой словарик, а не множество. Поэтому, нужно использовать set() без аргументов.

>>> is_it_a_set = {}
>>> type(is_it_a_set)
<class 'dict'>

>>> this_is_a_set = set()
>>> type(this_is_a_set)
<class 'set'>

Изменение множеств

Множества можно менять, добавляя или удаляя элементы. Так как они не упорядочены, то индексирование не имеет смысла и не поддерживается: мы не может получать доступ к элементам множества по индексу, как мы это делаем для списков и кортежей.

Добавление одного элемента выполняется методом add(). Нескольких элементов из коллекции или нескольких коллекций – методом update():

>>> my_set = {44, 55}
>>> my_set.add(50)
>>> my_set
{50, 44, 55}

>>> my_set.update([1, 2, 3])
>>> my_set
{1, 2, 3, 44, 50, 55}

>>> my_set.update([2, 3, 6], {1, 50, 60}) 
>>> my_set
{1, 2, 3, 6, 44, 50, 55, 60}

>>> my_set.update("string")
>>> my_set
{1, 2, 3, 6, 'i', 44, 'r', 50, 's', 55, 'n', 'g', 60, 't'}

Естественно, что при добавлении элементов дубликаты игнорируются.

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

Для удаления элемента существуют методы discard() и remove(). Делают они одно и тоже, но если удаляемого элемента нет во множестве, то discard() оставит множество неизменным молча, а remove() – бросит исключение:

>>> my_set = {1, 2, 3, 4, 5, 6}
>>> my_set.discard(2)
>>> my_set
{1, 3, 4, 5, 6}

>>> my_set.remove(4)
>>> my_set
{1, 3, 5, 6}

>>> my_set.discard(10)
>>> my_set
{1, 3, 5, 6}

>>> my_set.remove(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 10

Также есть метод pop(), который берет какой-нибудь (первый попавшийся) элемент множества, удаляет его и возвращает как результат:

>>> my_set = {3, 4, 5, 6, 1, 2}
>>> my_set
{1, 2, 3, 4, 5, 6}
>>> my_set.pop()
1
>>> my_set
{2, 3, 4, 5, 6}

Наконец, очистить множество (т.е. удалить все его элементы) можно методом clear():

>>> my_set = {1, 2, 3}
>>> my_set.clear()
>>> my_set
set()

Проверка членства

Узнать есть ли элемент в множестве очень легко оператором in (или not in, если хотим убедиться в отсутствии элемента):

>>> s = {"banana", "apple"}
>>> "banana" in s
True
>>> "tomato" not in s
True

Таким образом проверяется членства одного элемента, если нужно узнать является ли одно множество подмножеством другого, то оператор in тут не подойдет:

>>> {1, 2} in {1, 2, 3}
False

Тут подойдут операторы < и >. Чтобы получить True, с «широкой» стороны оператора должно стоять множество, полностью содержащее множество, стоящее по «узкую» сторону галочки:

>>> {1, 2} < {1, 2, 3, 4}
True
>>> {5, 6, 7, 8} > {5, 8}
True
>>> {1, 2, 3} < {1, 2, 4}
False

Итерация множеств

Пробежаться по элементам множества также легко, как и по элементам других коллекций оператором for-in (порядок обхода не определен точно):

my_set = {"Moscow", "Paris", "London"}
for elem in my_set:
    print(elem)
Moscow
London
Paris

Операции над множествами

Самое интересное – проводить математические операции над множествами.

Рассмотрим два множества A и B:

A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}

Объединение

Объединение множеств – множество, в котором есть все элементы одного и другого множеств. Это коммуникативная операция (от перемены мест ничего не меняется).

В Python используется либо метод union(), либо оператор вертикальная черта «|»:

>>> A = {1, 2, 3, 4, 5}
>>> B = {4, 5, 6, 7, 8}

>>> A | B
{1, 2, 3, 4, 5, 6, 7, 8}

>>> A.union(B)
{1, 2, 3, 4, 5, 6, 7, 8}

>>> B.union(A)
{1, 2, 3, 4, 5, 6, 7, 8}

Пересечение множеств

Пересечение множеств – множество, в которое входят только общие элементы, то есть которые есть и в первом, и во втором множестве. Также коммуникативная операция.

Пересечение вычисляют методом intersection() или оператором амперсандом «&»:

>>> A = {1, 2, 3, 4, 5}
>>> B = {4, 5, 6, 7, 8}

>>> A & B
{4, 5}

>>> B & A
{4, 5}

>>> A.intersection(B)
{4, 5}

Разность множеств

Разность множеств A и В – множество элементов из A, которых нет в B. Не коммуникативная операция!

Выполняется знаком минус «-» или оператором difference():

>>> A = {1, 2, 3, 4, 5}
>>> B = {4, 5, 6, 7, 8}

>>> A - B
{1, 2, 3}

>>> B - A
{8, 6, 7}

>>> A.difference(B)
{1, 2, 3}

>>> B.difference(A)
{8, 6, 7}

Как видно есть разница, в каком порядке идут операнды.

Симметричная разность

Симметричная разность – это объединение множеств за исключеним их пересечения. По другому, это сумма разностей. Это коммуникативный оператор.

Используется метод symmetric_difference() или оператор крышка «^»:

>>> A = {1, 2, 3, 4, 5}
>>> B = {4, 5, 6, 7, 8}

>>> A ^ B
{1, 2, 3, 6, 7, 8}

>>> B ^ A
{1, 2, 3, 6, 7, 8}

>>> A.symmetric_difference(B)
{1, 2, 3, 6, 7, 8}

Обратите внимание на эквивалентность операции определениям, которые я привел в начале этого раздела:

>>> A ^ B == (A - B) | (B - A)   # объединение простых разностей
True

>>> A ^ B == (A | B) - (A & B)   # разность объединения и пересечения
True

Прочее

Ко множествам можно применять стандартные функции all(), any(), enumerate(), len(), max(), min(), sorted(), sum(). Описания их ищите тут.

Прочие методы класса set:

copy() Возвращает копию множества
difference_update(other_set)Удаляет из этого множества все элементы, которые есть во множестве, переданным в аргументе
intersection_update(other_set)Обновляет это множество элементами из пересечения множеств
isdisjoint(other_set)Возвращает True, если множества не пересекаются
issubset(other_set)Возвращает True, если это множество является подмножеством другого
issuperset(other_set)Возвращает True, если это множество является надмножеством другого
symmetric_difference_update(other_set)Добавляет в это множество симметричную разность этого и другого множеств

Замороженное множество

Замороженное множество (frozen set) также является встроенной коллекцией в Python. Обладая характеристиками обычного множества, замороженное множество не может быть изменено после создания (подобно тому, как кортеж является неизменяемой версией списка).

Будучи изменяемыми, обычные множества являются нехешируемыми (unhashable type), а значит не могут применятся как ключи словаря или элементы других множеств.

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

Создаются замороженные множества функцией frozenset(), где аргументом будет другая коллекция. Примеры:

>>> A = frozenset({1, 2, 3})
>>> A
frozenset({1, 2, 3})

>>> B = frozenset(['a', 'b', 'cd'])
>>> B
frozenset({'cd', 'b', 'a'})

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

>>> A = frozenset('hello')
>>> B = frozenset('world')
>>> A | B
frozenset({'o', 'r', 'd', 'e', 'l', 'h', 'w'})
>>> A & B
frozenset({'o', 'l'})
>>> A ^ B
frozenset({'d', 'e', 'h', 'r', 'w'})

Теперь вы знаете много о множествах в Python.

Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈 

Добавить комментарий