Множини в Python
Множина – невпорядкована колекція об'єктів, що хешуються, які не повторюються.
Типове використання множин — перевірка певного елемента на входження в множину та видалення повторів елементів і виконання таких операцій, як об'єднання, пересічення, різниця і симетрична різниця.
В множинах немає поняття позиції елемента. Відповідно, вони не підтримують індексацію і зрізання.
Вбудованими в Python класами множин є:
- set (мутабельна множина).
- frozenset (немутабельна множина)
Створення множин
Створити множини в Python можна декількома способами.
Перерахування елементів (тільки set
)
Найпростіший спосіб — просто перерахувати елементи, які будуть складати множину. Елементи розділяються знаком коми і заключаються у фігурні дужки:
>>> my_set = {1, 5, 3, 9} # множина цілих чисел
>>> my_set
{1, 3, 5, 9}
>>> fruits = {'apple', 'orange', 'apple', 'orange', 'banana'} # множина символьних рядків, тільки унікальні елементи
>>> fruits
{'orange', 'banana', 'apple'}
>>>
Зауважте: таким способом можна створити тільки об'єкти класа set
!
Включення множин (тільки set
)
Множину можна створити за допомогою включень множин. Синтаксис дуже схожий на спискові включенні, різниця тільки у тому, що вираз заключається у фігурні дужки:
>>> number_set = {x*x for x in range(10) if x%2}
>>> number_set
{1, 9, 81, 49, 25}
>>>
>>> number_set = {i * j for i in range(3) for j in range(3)}
>>> number_set
{0, 1, 2, 4}
>>>
Зауважте: таким способом можна створити тільки об'єкти класа set
!
Створення за допомогою конструктора класа
Для початку створимо пусту множину, тобто таку, яка не містить жодного об'єкта:
>>> empty_set = set()
>>> empty_set
set()
>>> empty_frozenset = frozenset()
>>> empty_frozenset
frozenset()
>>>
Конструктору множини можна передати ітерабельний об'єкт:
>>> my_frozenset = frozenset([4, 1, 4, 3, 8])
>>> my_frozenset
frozenset({8, 1, 3, 4})
>>>
>>> letters = set('abrakadabra')
>>> letters
{'b', 'd', 'r', 'k', 'a'}
>>>
Множини у свою чергу теж є ітерабельними об'єктами:
>>> my_set = set(my_frozenset)
>>> my_set
{8, 1, 3, 4}
>>>
Давайте спробуємо створити множину передавши в конструктор генератор:
>>> def get_ints(n):
... for i in range(n):
... yield i
... yield i + 1
...
>>> list(get_ints(10)) # усі числа, які повертає генератор
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10]
>>> set(get_ints(10)) # множина містить тільки унікальні значенння
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
>>>
Операції з множинами
Операція | Опис |
---|---|
len(s) |
Кількість елементів множини |
x in s x not in s |
Перевірка чи входить об'єкт у множину |
s.isdisjoint(t) |
Перевірка того, що множина s не має спільних елементів з множиною t |
s.issubset(t) s <= t |
Перевірка того, що усі елементи множини s є елементами множини t |
s < t |
Перевірка того, що s<=t і s!=t |
s.isuperset(t) s >= t |
Перевірка того, що усі елементи множини t є елементами множини s |
s > t |
Перевірка того, що s>=t і `s!=t |
s.union(t, …) s | t | … |
створення нової множини, яка є об'єднанням даних множин |
s.intersection(t, …) s & t & … |
створення нової множини, яка є перетином даних множин |
s.difference(t, …) s - t - … |
створення нової множини, яка є різницею даних множин |
s.symmetric_difference(t) s ^ t |
створення нової множини, яка є симетричною різницею даних множин |
s.copy() |
Неповна копія множини s |
Як ви могли зауважити, що одні й ті ж операції можна викликати як бінарний оператор, так і як метод класа. Різниця полягає у наступному:
- операції над множинами, що є методами, приймають у якості аргументів будь-які ітерабельні об'єкти
- операції, які записані у вигляді бінарних операцій, потребують щоб другий операнд теж був множиною, і повертає множину того типу, якою була перша множина
Наступні операції використовуються тільки з мутабельними множинами:
Операція | Опис |
---|---|
s.update(t, …) s |= t | … |
додати до даної множини елементи з інших множин |
s.intersection_update(t, …) s &= t & … |
залишити у даній множині тільки ті елементи, що є і в інших множинах |
s.difference_update(t, …) s -= t | … |
видалити з даної множини ті елементи, що є в інших множинах |
s.symmetric_difference_update(t) s ^= t |
залишити або додати в s елементи, що є або в s , або в t , але не в обох множинах |
s.add(element) |
додати новий елемент у множину |
s.remove(element) |
Видалити елемент з множини. Якщо елемента у множині немає, піднімається виняток KeyError |
s.discard(element) |
Видалити елемент з множини якщо він належить цій множині |
s.pop() |
Видалити з множини і повернути довільний елемент. Якщо множина пуста, піднімається виняток KeyError |
s.clear() |
Видалити усі елементи з множини |
Розглянемо деякі операції з множинами.
Зміна множини
Метод add()
приймає один аргумент будь-якого немутабельного типу і додає його в множину.
>>> a_set = {1, 2}
>>> a_set.add(4)
>>> a_set
{1, 2, 4}
>>> len(a_set) # Тепер множина містить три елементи.
3
Якщо ви спробуєте додати до множини елемент, який там вже був, нічого не станеться. Не відбудеться ніякої помилки, і нічого не зміниться. Множина і далі міститиме три елементи.
Метод update()
приймає множину і додає всі елементи цієї множини до нашої. Це те ж саме, що викликати add()
для кожного елемента множини-параметра:
>>> a_set = {1, 2, 3}
>>> a_set
{1, 2, 3}
>>> a_set.update({2, 4, 6})
Також update()
можна викликати з довільним числом параметрів, це те ж саме, що викликати цей метод послідовно для кожного з них окремо:
>>> a_set.update({3, 6, 9}, {1, 2, 3, 5, 8, 13})
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 13}
Окрім множин, update()
можна передати ітерабельний об'єкт:
>>> a_set.update([10, 20, 30])
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 10, 13, 20, 30}
Видалення елементів з множини
Метод discard()
приймає єдине значення як параметр, і видаляє це значення з множини.
Якщо викликати discard() для значення, якого немає в множині, в ній нічого не зміниться. Не згенерується жодного винятку:
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set
{1, 3, 36, 6, 10, 45, 15, 21, 28}
>>> a_set.discard(10)
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.discard(10)
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
Метод remove()
також приймає єдине значення як аргумент, і також видаляє це значення з множини.
Якщо значення немає в множині, метод remove()
кидає виняток KeyError
:
>>> a_set.remove(21)
>>> a_set
{1, 3, 36, 6, 45, 15, 28}
>>> a_set.remove(21)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 21
Метод pop()
видаляє одне значення з множини і повертає його. Щоправда, оскільки множини - це невпорядковані набори, то "останнього" немає елемента — важко передбачити, який елемент буде повернуто.
Спроба виклику pop()
для порожньої множини створить виняток KeyError
:
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set.pop()
1
>>> a_set.pop()
3
>>> a_set.pop()
36
>>> a_set
{6, 10, 45, 15, 21, 28}
>>> a_set.pop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'pop from an empty set'
Метод clear()
видаляє всі значення множини, роблячи її порожньою:
>>> a_set.clear()
>>> a_set
set()
Теоретико-множинні операції
Розділ названо так тому, що зараз ми розглянемо ті дії з множинами, які вивчає відповідний розділ математики.
Щоб визначити, чи належить множині елемент, використовуйте оператор in/not in
:
>>> a_set = {2, 4, 5, 9, 12, 21, 30, 51, 76, 127, 195}
>>> 30 in a_set
True
>>> 31 in a_set
False
Метод union()
(об’єднання) повертає множину, що складається з елементів, які належать хоча б одній з двох множин:
>>> {1,2,3}.union([3,4,5])
{1, 2, 3, 4, 5}
>>>
Метод intersection()
(перетин) повертає множину, що складається з елементів, які належать одночасно двом множинам:
>>> {1,2,3}.intersection([2,3,4])
{2, 3}
>>>
Метод difference()
(різниця) повертає множину з тих елементів, які не належать переданій множині:
>>> {1,2,3,4,5}.difference([2,3,11])
{1, 4, 5}
>>>
Метод symmetric_difference
(симетрична різниця) повертає множину з тих елементів, які належать рівно одній з множин:
>>> {1,2,3,4,5}.symmetric_difference([2,3,11])
{1, 4, 5, 11}
>>>
Множини в булевому контексті
Тут, як завжди, все просто: порожня множина — False, а непорожня, не залежно від вмісту елементів — True.