#11 – Множества (set и frozenset)
Python содержит еще один формат списка, что позволяет хранить набор данных. Таким списком являются множества. В ходе урока мы научимся использовать множество «set», а также множество «frozenset».
Видеоурок
Множества схожи со списками, но имеют ряд отличий.
Во-первых, множества создаются в абсолютно случайном порядке. Вы можете разместить элементы как вам будет угодно, но они все равно будут расположены впоследствии в случайном порядке.
Во-вторых, множества не могут иметь повторяющихся элементов. Все элементы с одинаковым значением не будут выведены повторно.
Множества удобно использовать когда вы хотите удалить повторяющиеся элементы из списка, например:
some_list = [12, 56, 91, 12] set(some_list) # Результат: 12, 56, 91
Также для множеств существует огромное количество операций, которые приведены ниже:


Frozenset
Frozenset — метод, что позволяет создать, которое нельзя изменять в ходе выполнения программы. Получается, что Frozenset это смесь множества и кортежа.
Весь код будет доступен после подписки на проект!
Задание к уроку
Необходимо оформить подписку на проект, чтобы получить доступ ко всем домашним заданиям
Большое задание по курсу
Вам необходимо оформить подписку на сайте, чтобы иметь доступ ко всем большим заданиям. В задание входит методика решения, а также готовый проект с ответом к заданию.
PS: подобные задания доступны при подписке от 1 месяца
Python: Set/Frozenset (Множество)
Множество (класс set) — это контейнер, который содержит уникальные не повторяющиеся элементы в случайном порядке (неупорядоченная коллекция).
Что значит неупорядоченная? Это значит, что два множества эквивалентны, если содержат одинаковые элементы.

Элементы множества должны быть уникальными, множество не может содержать одинаковых элементов. Добавление элементов, которые уже есть в множестве, не изменяет это множество.

Для множеств используются фигурные скобки, как у словарей. Достаточно перечислить элементы в скобках.
mySet = # выводится в любом случайном порядке print(mySet) #
Но таким способом нельзя создать пустое множество, вместо него будет создан пустой словарь.
wrong_empty_set = <> print(type(wrong_empty_set)) #
Для создания пустого множества нужно непосредственно использовать set():
correct_empty_set = set() print(type(correct_empty_set)) #
Также в set() можно передать какой-либо объект, по которому можно пройтись (Iterable):
color_list = ["red", "green", "green", "blue", "purple", "purple"] color_set = set(color_list) print(color_set) # порядок может быть другим #
Число элементов вычисляется через len().
Существует ограничение, что элементами множества (как и ключами словарей) в Python могут быть только так называемые хешируемые (Hashable) объекты. Это обусловлено тем фактом, что внутренняя реализация set основана на хеш-таблицах. Например, списки и словари – это изменяемые объекты, которые не могут быть элементами множеств. Большинство неизменяемых типов в Python (int, float, str, bool, и т.д.) – хешируемые. Неизменяемые коллекции, например tuple, являются хешируемыми, если хешируемы все их элементы.
Проверить принадлежит ли какой-либо объект множеству можно с помощью оператора in.
tremendously_huge_set = if "green" in tremendously_huge_set: print("Green is there!") else: print("Unfortunately, there is no green. ")
Множество удобно использовать для удаления повторяющихся элементов. Создадим список с элементами, которые повторяются по несколько раз и сконвертируем его во множество. На этот раз множество создадим через метод set().
words = ['a', 'a', 'b', 'b', 'c', 'd', 'e'] mySet = set(words) print(str(mySet))
colors = for color in colors: print(color)
Два множества называются равными, если они состоят из одних и тех же элементов, порядок этих элементов не важен. Обратите внимание, что состав множеств отличается, но тем не менее они одинаковы (см. начало статьи).

my_cats = your_cats = print(my_cats == your_cats) # True
Если два множества не имеют общих элементов, то говорят, что эти множества не пересекаются и пересечение этих множеств является пустым множеством.

even_numbers = odd_numbers = # Очевидно, что множества чётных и нечётных чисел не пересекаются if even_numbers.isdisjoint(odd_numbers): print("Множества не пересекаются!") # Множества не пересекаются!
Подмножество множества S – это такое множество, каждый элемент которого является также и элементом множества S. Множество S в свою очередь является надмножеством исходного множества.

# Множество чисел Фибоначчи меньших 100 fibonacci_numbers = # Множество натуральных чисел меньших 100 natural_numbers = set(range(100)) # Множество чисел Фибоначчи является подмножеством множества # натуральных чисел if fibonacci_numbers.issubset(natural_numbers): print("Подмножество!") # Вывод: Подмножество! # В свою очередь множество натуральных чисел является # надмножеством множества чисел Фибоначчи if natural_numbers.issuperset(fibonacci_numbers): print("Надмножество!") # Вывод: Надмножество!
Пустое множество является подмножеством абсолютно любого множества. Само множество является подмножеством самого себя.
Другие методы: ‘clear’ (очистка множества), ‘copy’, ‘pop’ (удаляет первый элемент из множества. Так как множества не упорядочены, нельзя точно сказать, какой элемент будет первым), ‘remove’, ‘update’, ‘__bases__’, ‘__contains__’, ‘add’, ‘difference’, ‘difference_update’, ‘discard’, ‘intersection’ (пересечение), ‘intersection_update’, ‘isdisjoint’ (истина, если set и other не имеют общих элементов), ‘issubset’, ‘issuperset’, ‘symmetric_difference’, ‘symmetric_difference_update’, ‘union’ (объединение нескольких множеств).
У множеств можно находить объединение или пересечение элементов.
Объединение множеств – это множество, которое содержит все элементы исходных множеств. В Python есть несколько способов объединить множества.

my_fruits = your_fruits = # Для объединения множеств можно использовать оператор `|`, # оба операнда должны быть объектами типа set our_fruits = my_fruits | your_fruits print(our_fruits) # Вывод (порядок может быть другим): # Также можно использовать метод union. # Отличие состоит в том, что метод union принимает не только # объект типа set, а любой iterable-объект you_fruit_list: list = list(your_fruits) our_fruits: set = my_fruits.union(you_fruit_list) print(our_fruits) # Вывод (порядок может быть другим):
Добавление элементов в множество можно рассматривать как частный случай объединения множеств за тем исключением, что добавление элементов изменяет исходное множество, а не создаёт новый объект.
colors = # Метод add добавляет новый элемент в множество colors.add("purple") # Добавление элемента, который уже есть в множестве, не изменяет # это множество colors.add("red") print(colors) # Вывод (порядок может быть другим): # Метод update принимает iterable-объект (список, словарь, генератор и т.п.) # и добавляет все элементы в множество numbers = numbers.update(i**2 for i in [1, 2, 3]) print(numbers) # Вывод (порядок может быть другим):
Пересечение множеств – это множество, в котором находятся только те элементы, которые принадлежат исходным множествам одновременно.

def is_prime(number: int) -> bool: """ Возвращает True, если number - это простое число """ assert number > 1 return all(number % i for i in range(2, int(number**0.5) + 1)) def is_fibonacci(number: int) -> bool: """ Возвращает True, если number - это число Фибоначчи """ assert number > 1 a, b = 0, 1 while a + b
Разность двух множеств – это множество, в которое входят все элементы первого множества, не входящие во второе множество.

i_know: set = you_know: dict = < "Go": 0.4, "C++": 0.6, "Rust": 0.2, "Java": 0.9 ># Обратите внимание, что оператор `-` работает только # для объектов типа set you_know_but_i_dont = set(you_know) - i_know print(you_know_but_i_dont) # Вывод (порядок может быть другим): # Метод difference может работать с любым iterable-объектом, # каким является dict, например i_know_but_you_dont = i_know.difference(you_know) print(i_know_but_you_dont) # Вывод:
Удаление элемента из множества можно рассматривать как частный случай разности, где удаляемый элемент – это одноэлементное множество. Следует отметить, что удаление элемента, как и в аналогичном случае с добавлением элементов, изменяет исходное множество.
fruits = # Удаление элемента из множества. Если удаляемого элемента # нет в множестве, то ничего не происходит fruits.discard("orange") fruits.discard("pineapple") print(fruits) # Вывод (порядок может быть другим): # Метод remove работает аналогично discard, но генерирует исключение, # если удаляемого элемента нет в множестве fruits.remove("pineapple") # KeyError: "pineapple"
Симметрическая разность множеств – это множество, включающее все элементы исходных множеств, не принадлежащие одновременно обоим исходным множествам. Также симметрическую разность можно рассматривать как разность между объединением и пересечением исходных множеств.

non_positive = non_negative = # Обратите внимание, что оператор `^` может применяться # только для объектов типа set non_zero = non_positive ^ non_negative print(non_zero) # Вывод (порядок может быть другим):
Как видно из примера выше, число 0 принадлежит обоим исходным множествам, и поэтому оно не входит в результирующее множество. Для операции симметрической разности, помимо оператора ^, также существует два специальных метода – symmetric_difference и symmetric_difference_update. Оба этих метода принимают iterable-объект в качестве аргумента, отличие же состоит в том, что symmetric_difference возвращает новый объект-множество, в то время как symmetric_difference_update изменяет исходное множество.
non_positive = non_negative = range(4) non_zero = non_positive.symmetric_difference(non_negative) print(non_zero) # Вывод (порядок может быть другим): # Метод symmetric_difference_update изменяет исходное множество colors = colors.symmetric_difference_update(["green", "blue", "yellow"]) print(colors) # Вывод (порядок может быть другим):
frozenset
frozenset — это неизменяемое множество.
Методы: ‘__name__’, ‘copy’, ‘__bases__’, ‘__contains__’, ‘difference’, ‘intersection’, ‘isdisjoint’, ‘issubset’, ‘issuperset’, ‘symmetric_difference’, ‘union’.
Создадим два разных типа множества, сравним их и попытаемся добавить новые элементы.
setCat = set('кот') frozenCat = frozenset('кот') print(setCat == frozenCat) print(type(setCat)) # set print(type(frozenCat)) #frozenset setCat.add('э') # можем добавить print(setCat) frozenCat.add('e') # эта строка вызовет ошибку при компиляции
Множества и словари в Python
Множество ( set ) — встроенная структура данных языка Python, имеющая следующие свойства:
- множество — это коллекция Множество содержит элементы
- множество неупорядоченно Множество не записывает (не хранит) позиции или порядок добавления его элементов. Таким образом, множество не имеет свойств последовательности (например, массива): у элементов множества нет индексов, невозможно взять срез множества.
- элементы множества уникальны Множество не может содержать два одинаковых элемента.
- элементы множества — хешируемые объекты (hashable objects) В Python множество set реализовано с использованием хеш-таблицы. Это приводит к тому, что элементы множества должны быть неизменяемыми объектами. Например, элементом множества может быть строка, число, кортеж tuple , но не может быть список list , другое множество set .
Эти свойства множеств часто используются, чтобы проверять вхождение элементов, удаление дубликатов из последовательностей, а также для математических операций пересечения, объединения, разности.
Создание и изменение множества
Запустите в терминале Python в интерпретируемом режиме и проработайте примеры ниже.
Пустое множество создаётся с помощью функции set
>>> A = set() >>> type(A) >>> len(A) 0 >>> A set()
Обратите внимание, что размер множества множества можно получить с помощью функции len .
Добавим несколько элементов
>>> A.add(1) >>> A >>> A.add(2) >>> A >>> A.add(2) >>> A
Заметьте, что повторное добавление не имеет никакого эффекта на множество.
Также, из вывода видно, что литералом множества являются фигурные скобки <>, в которых через запятую указаны элементы. Так, ещё один способ создать непустое множество — воспользоваться литералом
>>> B = 1, 2> >>> B
При попытке добавления изменяемого объекта возникнет ошибка
>>> B.add([3,4,5]) Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: 'list'
Здесь произошла попытка добавить массив в множество B.
У операции добавления set.add существует обратная — операция удаления set.remove
>>> B >>> B.remove(1) >>> B >>> B.remove(3) Traceback (most recent call last): File "", line 1, in KeyError: 3
При попытке удаления элемента, не входящего в множество, возникает ошибка KeyError .
Однако, существует метод set.discard , который удаляет элемент из множества, только в том случае, если этот элемент присутствовал в нём.
Математические операции
Множества Python поддерживают привычные математические операции
Проверки
Чтобы проверить вхождение элемента в множество используйте логический оператор in
>>> B = 1, 2> >>> B >>> 3 in B False
Асимптотика x in set — O(1).
Стоит отметить, что оператор in работает и с другими коллекциями. Например, можно проверять вхождение подстроки в строку ‘AA’ in ‘bbAAcc’ или вхождение элемента в массив 5 in [1, 2, 5, 6] . Асимптотики в данном случае нужно уточнять в документации.
>>> A = 1, 2, 3> >>> B = 1, 2, 3> >>> A == B True >>> B.add(4) >>> A >>> B >>> A == B False
Проверка на нестрогое подмножество set.issubset
>>> A >>> B >>> A.issubset(B) True >>> B.issubset(A) False >>> A.issubset(A) True
Проверка на нестрогое надмножество set.issuperset
>>> A >>> B >>> A.issuperset(B) False >>> B.issuperset(A) True >>> B.issuperset(B) True
Операции получения новых множеств
>>> A = 1, 2, 4> >>> B = 1, 2, 3> >>> A.union(B) # union — объединение множеств >>> A.intersection(B) # intersection — пересечение >>> A.difference(B) # difference — разность множеств >>> B.difference(A) >>> A.symmetric_difference(B) # symmetric_difference — симметрическая разность >>> B.symmetric_difference(A)
Сводная таблица по множествам (cheatsheet)
Кроме того, у операций, порождающих новые множества, существует inplace варианты. Для методов это те же названия, только с префиксом _update, а для соответствующих операторов добавляется знак равенства =. Ниже показан вариант для операции разности множеств
>>> A = 1, 2, 3, 4> >>> B = 2, 4> >>> A.difference_update(B) >>> A >>> A = 1, 2, 3, 4> >>> B = 2, 4> >>> A -= B >>> A
Неизменяемые множества
В Python существует неизменяемая версия множества — frozenset . Этот тип объектов поддерживает все операции обычного множества set , за исключением тех, которые его меняют.
Неизменяемые множества являются хешируемыми объектами, поэтому они могут быть элементами множества set . Так можно реализовать, например, множество множеств, где множество set состоит из множеств типа frozenset .
Для создания frozenset используется функция frozenset(iterable) , в качестве аргумента принимающая итерирумый объект.
>>> FS = frozenset(1, 2, 3>) >>> FS frozenset() >>> A = 1, 2, 4> >>> FS & A frozenset() >>> A & FS
В этом примере показано создание frozenset из обычного множества . Обратите внимание на тип возвращаемого объекта для операции пересечения & . Возвращаемый объект имеет тип, соответствующий типу первого аргумента. Такое же поведение будет и с другими операциями над множествами.
Словари Python
Словарь (dictionary) в Python — это ассоциативный массив, реализовать который вы пробовали на прошлом занятии. Ассоциативный массив это структура данных, содержащая пары вида ключ:значение. Ключи в ассоциативном массиве уникальны.
В Python есть встроенный ассоциативный массив — dict . Его реализация основана на хеш-таблицах. Поэтому
- ключом может быть только хешируемый объект
- значением может быть любой объект
Создание и изменение словаря
Пустой словарь можно создать двумя способами:
>>> d1 = dict() >>> d2 = <> >>> d1 <> >>> d2 <> >>> type(d1) >>> type(d2)
Добавить элемент в словарь можно с помощью квадратных скобок:
>>> domains = <> >>> domains[‘ru’] = ‘Russia’ >>> domains[‘com’] = ‘commercial’ >>> domains[‘org’] = ‘organizations’ >>> domains
Из этого примера видно, что литералом словаря являются фигурные скобки, в которых через запятую перечислены пары в формате ключ:значение . Например, словарь domains можно было создать так domains = .
Доступ к элементу осуществляется по ключу:
>>> domains['com'] 'commercial' >>> domains['de'] Traceback (most recent call last): File "", line 1, in KeyError: 'de'
Удалить элемент можно с помощью оператора del . Если ключа в словаре нет, произойдет ошибка KeyError
>>> domains >>> del domains[‘de’] Traceback (most recent call last): File «», line 1, in KeyError: ‘de’ >>> del domains[‘ru’] >>> domains
Кроме того, для добавления, получения и удаления элементов есть методы dict.setdefault , dict.get , dict.pop , которые задействует дополнительный аргумент на случай, если ключа в словаре нет
>>> d1 = <> >>> d1.setdefault(‘a’, 10) 10 >>> d1.setdefault(‘b’, 20) 20 >>> d1 >>> d1.setdefault(‘c’) >>> d1 >>> d1.setdefault(‘a’, 123) 10 >>> d1 >>> d1.get(‘a’) 10 >>> d1.get(‘d’) # вернул None >>> d1.get(‘d’, ‘NoKey’) ‘NoKey’ >>> d1.pop(‘d’) Traceback (most recent call last): File «», line 1, in KeyError: ‘d’ >>> d1.pop(‘d’, 255) 255 >>> d1 >>> d1.pop(‘a’, 255) 10 >>> d1
Примечание о числовых ключах
Ключом может являться и число: int или float . Однако при работе со словарями в Python помните, что два ключа разные, если для них верно k1 != k2 # True .
>>> d = 0: 10> >>> d >>> d[0] = 22 >>> d >>> d[0.0] = 33 >>> d >>> 0.0 != 0 False
Поэтому при возможности избегайте в качестве ключей float -объектов.
Использование DictView: циклы и множественные операции
Если попробовать пройтись в цикле по словарю, то это будет проход по ключам
>>> d = 'a': 10, 'c': 30, 'b': 20> >>> for k in d: . print(k) . a c b
Зачастую необходимо пройтись в цикле по ключам, значениям или парам ключ:значение, содержащиеся в словаре. Для этого существуют методы dict.keys() , dict.values() , dict.items() . Они возвращают специальные DictView объекты, которые можно использовать в циклах:
>>> d = 'a': 10, 'c': 30, 'b': 20> >>> for k in d.keys(): . print(k) . a c b >>> for v in d.values(): . print(v) . 10 30 20 >>> for k, v in d.items(): . print(k, v) . a 10 c 30 b 20
Объекты DictView , содержащие только ключи, ведут себя подобно множествам. Кроме того, если DictView объекты для значений или пар содержат неизменяемые объекты, тогда они тоже ведут себя подобно множествам. Это означает, что привычные для множеств операции пересечения, вхождения и другие также работают с DictView .
>>> d >>> dkeys = d.keys() >>> ‘abc’ in dkeys False >>> ‘c’ in dkeys True >>> ‘a’, ‘b’, ‘c’> == dkeys True >>> dkeys & ‘b’, ‘c’, ‘d’>
Словарь с упорядоченными ключами OrderedDict
Это может понадобится для отправки задач на ejudge.
Если внимательно просмотреть примеры на циклы выше, то видно, что порядок итерирования в циклах совпадает с порядком добавления элементов в словарь.
Однако, такое поведение у стандартных словарей dict гарантируется, начиная с версии 3.7 (лабораторные примеры были сделаны из-под версии 3.7.4). Узнать свою версию Python можно, например, из терминала python3 --version или зайдя в интерпретируемый режим (версия будет написана сверху).
Если для вашей программы важно упорядочивание элементов, но вы не знаете, какой версии интерпретатор будет исполнять ваш скрипт, то вам нужно воспользоваться упорядоченной версией словарей OrderedDict .
Она находится в стандартной библиотеке collections .
Упорядоченный словарь поддерживает все операции, что и обычный словарь.
>>> import collections >>> od = collections.OrderedDict() >>> od OrderedDict() >>> od['a'] = 10 >>> od['c'] = 30 >>> od['b'] = 20 >>> od OrderedDict([('a', 10), ('c', 30), ('b', 20)])
Сайт построен с использованием Pelican. За основу оформления взята тема от Smashing Magazine. Исходные тексты программ, приведённые на этом сайте, распространяются под лицензией GPLv3, все остальные материалы сайта распространяются под лицензией CC-BY.
Чем отличаются set и frozenset
Множество (set) представляют еще один вид набора, который хранит только уникальные элементы. Для определения множества используются фигурные скобки, в которых перечисляются элементы:
users = print(users) #
Обратите внимание, что несмотря на то, что функция print вывела один раз элемент «Tom», хотя в определении множества этот элемент содержится два раза. Все потому что множество содержит только уникальные значения.
Также для определения множества может применяться функция set() , в которую передается список или кортеж элементов:
people = [«Mike», «Bill», «Ted»] users = set(people) print(users) #
Функцию set удобно применять для создания пустого множества:
users = set()
Для получения длины множества применяется встроенная функция len() :
users = print(len(users)) # 3
Добавление элементов
Для добавления одиночного элемента вызывается метод add() :
users = set() users.add("Sam") print(users)
Удаление элементов
Для удаления одного элемента вызывается метод remove() , в который передается удаляемый элемент. Но следует учитывать, что если такого элемента не окажется в множестве, то будет сгенерирована ошибка. Поэтому перед удалением следует проверять на наличие элемента с помощью оператора in :
users = user = «Tom» if user in users: users.remove(user) print(users) #
Также для удаления можно использовать метод discard() , который не будет генерировать исключения при отсутствии элемента:
users = users.discard(«Tim») # элемент «Tim» отсутствует, и метод ничего не делает print(users) # users.discard(«Tom») # элемент «Tom» есть, и метод удаляет элемент print(users) #
Для удаления всех элементов вызывается метод clear() :
users.clear()
Перебор множества
Для перебора элементов можно использовать цикл for:
users = for user in users: print(user)
При переборе каждый элемент помещается в переменную user.
Операции с множествами
С помощью метода copy() можно скопировать содержимое одного множества в другую переменную:
users = students = users.copy() print(students) #
Объединение множеств
Метод union() объединяет два множества и возвращает новое множество:
users = users2 = users3 = users.union(users2) print(users3) #
Пересечение множеств
Пересечение множеств позволяет получить только те элементы, которые есть одновременно в обоих множествах. Метод intersection() производит операцию пересечения множеств и возвращает новое множество:
users = users2 = users3 = users.intersection(users2) print(users3) #
Вместо метода intersection мы могли бы использовать операцию логического умножения:
users = users2 = print(users & users2) #
В этом случае мы получили бы тот же результат.
Модификация метода — intersection_update() заменяет пересеченными элементами первое множество:
users = users2 = users.intersection_update(users2) print(users) #
Разность множеств
Еще одна операция — разность множеств возвращает те элементы, которые есть в первом множестве, но отсутствуют во втором. Для получения разности множеств можно использовать метод difference или операцию вычитания:
users = users2 = users3 = users.difference(users2) print(users3) # print(users — users2) #
Отдельная разновидность разности множеств — симметрическая разность производится с помощью метода symmetric_difference() или с помощью операции ^ . Она возвращает все элементы обоих множеств за исключением общих:
users = users2 = users3 = users.symmetric_difference(users2) print(users3) # users4 = users ^ users2 print(users4) #
Отношения между множествами
Метод issubset позволяет выяснить, является ли текущее множество подмножеством (то есть частью) другого множества:
users = superusers = print(users.issubset(superusers)) # True print(superusers.issubset(users)) # False
Метод issuperset , наоборот, возвращает True, если текущее множество является надмножеством (то есть содержит) для другого множества:
users = superusers = print(users.issuperset(superusers)) # False print(superusers.issuperset(users)) # True
frozen set
Тип frozen set является видом множеств, которое не может быть изменено. Для его создания используется функция frozenset :
users = frozenset()
В функцию frozenset передается набор элементов — список, кортеж, другое множество.
В такое множество мы не можем добавить новые элементы, как и удалить из него уже имеющиеся. Собственно поэтому frozen set поддерживает ограниченный набор операций:
- len(s) : возвращает длину множества
- x in s : возвращает True, если элемент x присутствует в множестве s
- x not in s : возвращает True, если элемент x отсутствует в множестве s
- s.issubset(t) : возвращает True, если t содержит множество s
- s.issuperset(t) : возвращает True, если t содержится в множестве s
- s.union(t) : возвращает объединение множеств s и t
- s.intersection(t) : возвращает пересечение множеств s и t
- s.difference(t) : возвращает разность множеств s и t
- s.copy() : возвращает копию множества s
