Освой Python играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Python: Set/Frozenset (Множество)

В статье использовались текст и картинки из Python и теория множеств

Множество (класс set) - это контейнер, который содержит уникальные не повторяющиеся элементы в случайном порядке (неупорядоченная коллекция).

Что значит неупорядоченная? Это значит, что два множества эквивалентны, если содержат одинаковые элементы.

Set

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

Set

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


mySet = {'c', 'a', 't'}
# выводится в любом случайном порядке
print(mySet) # {'t', 'c', 'a'} 

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


wrong_empty_set = {}
print(type(wrong_empty_set))

# <class "dict">

Для создания пустого множества нужно непосредственно использовать set():


correct_empty_set = set()
print(type(correct_empty_set))

# <class "set">

Также в set() можно передать какой-либо объект, по которому можно пройтись (Iterable):


color_list = ["red", "green", "green", "blue", "purple", "purple"]
color_set = set(color_list)
print(color_set)

# порядок может быть другим
# {"red", "purple", "blue", "green"}

Число элементов вычисляется через len().

Существует ограничение, что элементами множества (как и ключами словарей) в Python могут быть только так называемые хешируемые (Hashable) объекты. Это обусловлено тем фактом, что внутренняя реализация set основана на хеш-таблицах. Например, списки и словари – это изменяемые объекты, которые не могут быть элементами множеств. Большинство неизменяемых типов в Python (int, float, str, bool, и т.д.) – хешируемые. Неизменяемые коллекции, например tuple, являются хешируемыми, если хешируемы все их элементы.

Проверить принадлежит ли какой-либо объект множеству можно с помощью оператора in.


tremendously_huge_set = {"red", "green", "blue"}

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 = {"red", "green", "blue"}

for color in colors:
    print(color)

Два множества называются равными, если они состоят из одних и тех же элементов, порядок этих элементов не важен. Обратите внимание, что состав множеств отличается, но тем не менее они одинаковы (см. начало статьи).

Equal sets

my_cats = {"bear", "pig", "cat", "cat"}
your_cats = {"pig", "pig", "bear", "cat", "cat"}
print(my_cats == your_cats)

# True

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

Set

even_numbers = {i for i in range(10) if i % 2 == 0}
odd_numbers = {i for i in range(10) if i % 2 == 1}

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

# Множества не пересекаются!

Подмножество множества S – это такое множество, каждый элемент которого является также и элементом множества S. Множество S в свою очередь является надмножеством исходного множества.

Set

# Множество чисел Фибоначчи меньших 100
fibonacci_numbers = {0, 1, 2, 3, 34, 5, 8, 13, 21, 55, 89}

# Множество натуральных чисел меньших 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 есть несколько способов объединить множества.

Set

my_fruits = {"apple", "orange"}
your_fruits = {"orange", "banana", "pear"}

# Для объединения множеств можно использовать оператор `|`,
# оба операнда должны быть объектами типа set
our_fruits = my_fruits | your_fruits
print(our_fruits)

# Вывод (порядок может быть другим):
{"apple", "banana", "orange", "pear"}

# Также можно использовать метод union.
# Отличие состоит в том, что метод union принимает не только
# объект типа set, а любой iterable-объект
you_fruit_list: list = list(your_fruits)
our_fruits: set = my_fruits.union(you_fruit_list)
print(our_fruits)

# Вывод (порядок может быть другим):
{"apple", "banana", "orange", "pear"}

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


colors = {"red", "green", "blue"}

# Метод add добавляет новый элемент в множество
colors.add("purple")
# Добавление элемента, который уже есть в множестве, не изменяет
# это множество
colors.add("red")
print(colors)

# Вывод (порядок может быть другим):
{"red", "green", "blue", "purple"}

# Метод update принимает iterable-объект (список, словарь, генератор и т.п.)
# и добавляет все элементы в множество
numbers = {1, 2, 3}
numbers.update(i**2 for i in [1, 2, 3])
print(numbers)

# Вывод (порядок может быть другим):
{1, 2, 3, 4, 9}

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

Set

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 < number:
        a, b = b, a + b
    return a + b == number

# Множество простых чисел до 100
primes = set(filter(is_prime, range(2, 101)))

# Множество чисел Фибоначчи до 100
fibonacci = set(filter(is_fibonacci, range(2, 101)))

# Множество простых чисел до 100, которые одновременно являются
# числами Фибоначчи
prime_fibonacci = primes.intersection(fibonacci)

# Или используя оператор `&`, который определён для множеств
prime_fibonacci = fibonacci & primes

print(prime_fibonacci)

# Вывод (порядок может быть другим):
{2, 3, 5, 13, 89}

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

Set

i_know: set = {"Python", "Go", "Java"}
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)

# Вывод (порядок может быть другим):
{"Rust", "C++"}

# Метод difference может работать с любым iterable-объектом,
# каким является dict, например
i_know_but_you_dont = i_know.difference(you_know)
print(i_know_but_you_dont)

# Вывод:
{"Python"}

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


fruits = {"apple", "orange", "banana"}

# Удаление элемента из множества. Если удаляемого элемента
# нет в множестве, то ничего не происходит
fruits.discard("orange")
fruits.discard("pineapple")
print(fruits)

# Вывод (порядок может быть другим):
{"apple", "banana"}

# Метод remove работает аналогично discard, но генерирует исключение,
# если удаляемого элемента нет в множестве
fruits.remove("pineapple")  # KeyError: "pineapple"

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

Set

non_positive = {-3, -2, -1, 0}
non_negative = {0, 1, 2, 3}

# Обратите внимание, что оператор `^` может применяться
# только для объектов типа set
non_zero = non_positive ^ non_negative
print(non_zero)

# Вывод (порядок может быть другим):
{-1, -2, -3, 1, 2, 3}

Как видно из примера выше, число 0 принадлежит обоим исходным множествам, и поэтому оно не входит в результирующее множество. Для операции симметрической разности, помимо оператора ^, также существует два специальных метода – symmetric_difference и symmetric_difference_update. Оба этих метода принимают iterable-объект в качестве аргумента, отличие же состоит в том, что symmetric_difference возвращает новый объект-множество, в то время как symmetric_difference_update изменяет исходное множество.


non_positive = {-3, -2, -1, 0}
non_negative = range(4)

non_zero = non_positive.symmetric_difference(non_negative)
print(non_zero)

# Вывод (порядок может быть другим):
{-1, -2, -3, 1, 2, 3}

# Метод symmetric_difference_update изменяет исходное множество
colors = {"red", "green", "blue"}
colors.symmetric_difference_update(["green", "blue", "yellow"])
print(colors)

# Вывод (порядок может быть другим):
{"red", "yellow"}

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') # эта строка вызовет ошибку при компиляции
Реклама