Множества (sets) в Python

Синтаксис

empty_set = set() # инициализирует пустое множество

literal_set = {'foo', 'bar', 'baz'} # строит множество с 3мя строками внутри

set_from_list = set(['foo', 'bar', 'baz']) # вызов функции set() для создания нового множества

set_from_iter = set(x for x in range(30)) # используются произвольные итерации для создания нового множества

set_from_iter = {x for x in [random.randint(0,10) for i in range(10)]} # альтернативная нотация
Замечания

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

Множества изменяемы и поэтому не могут быть хэшированы, поэтому вы не можете их использовать в качестве ключей словаря или помещать их в другие наборы или где-либо еще, где требуются хэшированые типы. В таких случаях вы может использовать неизменяемое множество  frozenset. Элементы множества должны быть способны к хэшированию. Это значит, что они должны иметь корректный метод __hash__, который совместим с  __eq__. В общем, изменяемые типы такие как list или set не способны к хэшированию и не могут быть помещены во множество. Если вы столкнулись с этой проблемой, рассмотрите использование dict и неизменяемые ключи.

Получить уникальные элементы списка

Допустим, у вас есть список ресторанов - может быть, вы читаете его из файла. Вы заботитесь об уникальных ресторанах в списке. Лучший способ получить уникальные элементы из списка - превратить его во множество:

restaurants = ["McDonald's", "Burger King", "McDonald's", "Chicken Chicken"]
unique_restaurants = set(restaurants)
print(unique_restaurants)

>>>Out: {'Burger King', "McDonald's", 'Chicken Chicken'}

Обратите внимание, что набор не в том же порядке, что и исходный список; это потому , что множества являются неупорядоченными, так же , как dict.

Это может быть легко преобразован обратно в List с Python встроенный в list функций, что дает еще один список , который тот же список , как оригинал , но без дубликатов:

list(unique_restaurants)

>>>Out: ['Burger King', "McDonald's", 'Chicken Chicken']

 

Также принято видеть это одной строкой:

# удалены все дубликаты и возвращается другой список
list(set(restaurants))

 

Теперь любые операции, которые могут быть выполнены в исходном списке, могут быть выполнены снова.

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

с другими наборами

 # Пересечения множеств
{1, 2, 3, 4, 5}.intersection({3, 4, 5, 6})

>>>Out: {3, 4, 5}

{1, 2, 3, 4, 5} & {3, 4, 5, 6}

>>>Out: {3, 4, 5}

# Объединение множеств
{1, 2, 3, 4, 5}.union({3, 4, 5, 6})

>>>Out: {1, 2, 3, 4, 5, 6}

{1, 2, 3, 4, 5} | {3, 4, 5, 6}

>>>Out: {1, 2, 3, 4, 5, 6}


# Разница множеств
{1, 2, 3, 4}.difference({2, 3, 5})

>>>Out: {1, 4}

{1, 2, 3, 4} - {2, 3, 5}

>>>Out: {1, 4}

# Симметрическая разность множеств
{1, 2, 3, 4}.symmetric_difference({2, 3, 5})

>>>Out: {1, 4, 5}

{1, 2, 3, 4} ^ {2, 3, 5}

>>>Out: {1, 4, 5}

# проверка суперпозиции множеств
{1, 2}.issuperset({1, 2, 3})

>>>Out: False

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

>>>Out: False

# проверка подмножества
{1, 2}.issubset({1, 2, 3})

>>>Out: True

{1, 2} <= {1, 2, 3}

>>>Out: True

# проверка непересечения
{1, 2}.isdisjoint({3, 4})

>>>Out: True

{1, 2}.isdisjoint({1, 4})

>>>Out: False

 

с отдельными элементами

# проверка наличия элемента в множестве
2 in {1,2,3}

>>>Out: True

4 in {1,2,3}

>>>Out: False

4 not in {1,2,3}

>>>Out: True

# Доблавение и удаление из множества
s = {1,2,3}
s.add(4)
s == {1,2,3,4}

>>>Out: True

s.discard(3)
s == {1,2,4}

>>>Out: True

s.discard(5)  #элемента 5 нет, поэтому если его не учитывать ничего не произойдет
s == {1,2,4}

>>>Out: True

s.remove(2)
s == {1,4}

>>>Out: True

s.remove(2) #нельзя удалить элемент 2, т.к. его уже нет в множестве

>>>Out: KeyError: 2 #

Операции множества возвращают новые множества, но имеют соответствующие комбинированные операторы присваивания:

Метод Операция на месте метод на месте
объединение s|= t update
пересечение s &= t intersection_update
разница s -= t difference_update
симметрическая разница s ^ = t symmetric_difference_update

Например:

s = {1, 2}
s.update({3, 4})
s == {1, 2, 3, 4}

>>>Out: True

Множество множеств

 {{1,2}, {3,4}} #приводит к ошибке
 
>>>Out: TypeError: unhashable type: 'set'

Вместо этого используйте frozenset :

 {frozenset({1, 2}), frozenset({3, 4})}

Операции над множествами с использованием методов и встроенных функций

Определим два множества a и b

a = {1, 2, 2, 3, 4}
b = {3, 3, 4, 4, 5}

a.intersection(b)

>>>Out: {3, 4} 

объединение

a.union(b) возвращает новый набор с элементами, присутствующими в любом a и b

a.union(b)

>>>Out: {1, 2, 3, 4, 5} 

разница

a.difference(b) возвращает новый набор с элементами , присутствующими в , но не в a b

a.difference(b)

>>>Out: {1, 2}

b.difference(a)

>>>Out: {5}

Симметричная разница

a.symmetric_difference(b) возвращает новый набор с элементами, присутствующими в любом a или b, но не в обоих множествах

a.symmetric_difference(b)

>>>Out: {1, 2, 5}

b.symmetric_difference(a)

>>>Out: {1, 2, 5}

Примечание: a.symmetric_difference(b) == b.symmetric_difference(a)

Подмножество и суперсет

c.issubset(a) проверяет, соответствует ли каждый элемент c в a

a.issuperset(c) проверяет, является ли каждый элемент c находится d a.

c = {1, 2}
c.issubset(a)

>>>Out: True

a.issuperset(c)

>>>Out: True 

Последние операции имеют эквивалентные операторы, как показано ниже:

Метод Оператор
a.intersection(b) a & b
a.union(b) a | b
a.difference(b) a - b
a.symmetric_difference(b) a ^ b
a.issubset(b) a <= b
a.issuperset(b) a >= b

Несвязные множества

Множества a и d не пересекаются, если ни один элемент a не находится также в d , и наоборот.

d = {5, 6}
a.isdisjoint(b) # {2, 3, 4} are in both sets

>>>Out: False

a.isdisjoint(d)

>>>Out: True

# это эквивалентная проверка, но менее эффективная

len(a & d) == 0
>>>Out: True

# еще менее эффективная
a & d == set()

>>>Out: True 

Тестирование членства

Встроенный ключевое слово in ищет вхождения

1 in a

>>>Out: True

6 in a

>>>Out: False 

длина

Встроенная функция len() возвращает количество элементов в множестве

len(a)

>>>Out: 4

len(b)

>>>Out: 3

Множества против мультимножеств

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

Рассмотрим этот пример:

setA = {'a','b','b','c'}
setA

>>>Out: set(['a', 'c', 'b'])

 

При сохранении строки 'a' , 'b' , 'b' , 'c' в структуру набора данных , мы потеряли информацию о том , что 'b' встречается дважды. Конечно, сохранение элементов в списке сохранит эту информацию.

listA = ['a','b','b','c']
listA

>>>Out: ['a', 'b', 'b', 'c']

 

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

from collections import Counter
counterA = Counter(['a','b','b','c'])
counterA

>>>Out: Counter({'b': 2, 'a': 1, 'c': 1})

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