ТЕОРИЯ И ПРАКТИКА
-
Ввод и вывод данных
- Задачи
-
Условия
- Задачи
-
Цикл for
- Задачи
-
Строки
- Задачи
-
Цикл while
- Задачи
-
Списки
- Задачи
-
Двумерные массивы
- Задачи
-
Словари
- Задачи
-
Множества
- Задачи
-
Функции и рекурсия
- Задачи
Занятие 9. Множества в питоне
Введение в множества
Множества в Python: структурные типы данных
Множество (set) в Python — это встроенная коллекция, которая хранит набор уникальных элементов. Подобно спискам и словарям, множества являются одним из основных инструментов для хранения и обработки данных.
Основные свойства множеств
Неупорядоченность
Элементы в множестве не имеют определенного порядка. Это означает, что вы не можете обратиться к элементу по индексу, как в списке. При каждом выводе множества на экран порядок элементов может меняться.
Уникальность элементов
Это ключевое свойство множеств. Внутри одного множества каждый элемент может встречаться только один раз. Если вы попытаетесь добавить существующий элемент, множество просто не изменится.
Изменяемость
Стандартные множества (set) являются изменяемыми, то есть вы можете добавлять и удалять из них элементы после создания. Однако сами элементы множества должны быть неизменяемыми (например, числа, строки, кортежи). Вы не можете добавить список или другое множество в качестве элемента.
Отличие множеств от списков и словарей
- От списков (
list): Множества неупорядочены и содержат только уникальные элементы, в то время как списки упорядочены и могут содержать дубликаты. - От словарей (
dict): Словари хранят пары "ключ-значение", а множества хранят только отдельные элементы. Можно сказать, что множество похоже на словарь, у которого есть только ключи.
Основные сценарии применения множеств
Множества идеально подходят для задач, где важна уникальность и высокая скорость проверки на принадлежность элемента.
- Удаление дубликатов из коллекций.
- Быстрая проверка, содержится ли элемент в наборе данных.
- Выполнение математических операций: объединение, пересечение, разность.
Создание множеств в Python
Пустое множество через set()
Чтобы создать пустое множество, используйте функцию set(). Обратите внимание, что использование пустых фигурных скобок {} создаст пустой словарь, а не множество.
# Правильный способ создать пустое множество
empty_s = set()
print(type(empty_s)) # <class 'set'>
# Это создаст пустой словарь!
empty_dict = {}
print(type(empty_dict)) # <class 'dict'>
Множество с элементами через {}
Вы можете создать множество, перечислив его элементы в фигурных скобках. Дубликаты будут автоматически удалены.
# Создание множества с элементами
fruits = {'apple', 'banana', 'orange', 'apple'}
print(fruits) # {'orange', 'banana', 'apple'} - порядок не гарантирован
Преобразование других структур в множество
Вы можете легко создать множество из любой итерируемой структуры данных (список, строка, кортеж) с помощью функции set().
Из списка
Это самый популярный способ удалить дубликаты из списка.
numbers_list = [1, 2, 3, 2, 4, 1, 5]
unique_numbers = set(numbers_list)
print(unique_numbers) # {1, 2, 3, 4, 5}
Извлечение уникальных символов из строки
greeting = "hello world"
unique_chars = set(greeting)
print(unique_chars) # {'h', 'w', 'r', 'l', 'd', ' ', 'o', 'e'}
Извлечение элементов из кортежа
colors_tuple = ('red', 'green', 'blue', 'red')
unique_colors = set(colors_tuple)
print(unique_colors) # {'blue', 'green', 'red'}
Извлечение ключей из словаря
При преобразовании словаря в множество в него попадут только ключи.
user_data = {'name': 'Alice', 'age': 30, 'city': 'New York'}
keys_set = set(user_data)
print(keys_set) # {'name', 'age', 'city'}
Добавление элементов
Метод add()
Метод add() добавляет один элемент в множество. Если элемент уже существует, множество не изменится.
numbers = {1, 2, 3}
numbers.add(4)
print(numbers) # {1, 2, 3, 4}
numbers.add(2) # Попытка добавить существующий элемент
print(numbers) # {1, 2, 3, 4} - ничего не изменилось
Метод update()
Метод update() позволяет добавить сразу несколько элементов из любой итерируемой структуры (другого множества, списка, строки).
s1 = {'a', 'b'}
s2 = ['b', 'c', 'd']
s1.update(s2)
print(s1) # {'a', 'c', 'b', 'd'}
s1.update('xyz')
print(s1) # {'a', 'd', 'x', 'y', 'z', 'b', 'c'}
Удаление элементов
Метод remove()
Метод remove() удаляет указанный элемент. Если такого элемента в множестве нет, он вызовет ошибку KeyError.
items = {'pen', 'pencil', 'eraser'}
items.remove('pencil')
print(items) # {'pen', 'eraser'}
# items.remove('ruler') # Это вызовет ошибку KeyError, т.к. 'ruler' нет в множестве
Метод discard()
Метод discard() также удаляет элемент, но в отличие от remove(), он не вызовет ошибку, если элемента не существует. Это более безопасный способ удаления.
items = {'pen', 'pencil', 'eraser'}
items.discard('pen')
print(items) # {'pencil', 'eraser'}
items.discard('ruler') # Ошибки не будет
print(items) # {'pencil', 'eraser'} - ничего не изменилось
Метод pop()
Метод pop() удаляет и возвращает произвольный элемент из множества. Так как множества неупорядочены, вы не можете предсказать, какой именно элемент будет удален. Если множество пусто, pop() вызовет ошибку KeyError.
data = {10, 20, 30, 40}
removed_element = data.pop()
print(f"Удаленный элемент: {removed_element}")
print(f"Оставшееся множество: {data}")
Метод clear()
Метод clear() удаляет все элементы из множества, делая его пустым.
items = {'pen', 'pencil', 'eraser'}
items.clear()
print(items) # set()
Операции над множествами
Объединение
Оператор | (логическое ИЛИ)
Возвращает новое множество, содержащее все уникальные элементы из обоих множеств.
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
union_set = set_a | set_b
print(union_set) # {1, 2, 3, 4, 5, 6}
Метод union()
Работает аналогично оператору |, но может принимать в качестве аргумента любую итерируемую структуру.
set_a = {1, 2, 3}
list_b = [3, 4, 5]
union_set = set_a.union(list_b)
print(union_set) # {1, 2, 3, 4, 5}
Пересечение
Оператор & (логическое И)
Возвращает новое множество, содержащее только те элементы, которые есть в обоих множествах.
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
intersection_set = set_a & set_b
print(intersection_set) # {3, 4}
Метод intersection()
Аналог оператора &.
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
intersection_set = set_a.intersection(set_b)
print(intersection_set) # {3, 4}
Разность
Оператор -
Возвращает новое множество, содержащее элементы, которые есть в первом множестве, но отсутствуют во втором.
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
difference_set = set_a - set_b
print(difference_set) # {1, 2}
difference_set_2 = set_b - set_a
print(difference_set_2) # {5, 6}
Метод difference()
Аналог оператора -.
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
difference_set = set_a.difference(set_b)
print(difference_set) # {1, 2}
Симметрическая разность
Оператор ^ (логическое исключающее ИЛИ)
Возвращает новое множество, содержащее элементы, которые есть в одном из множеств, но не в обоих сразу.
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
sym_diff_set = set_a ^ set_b
print(sym_diff_set) # {1, 2, 5, 6}
Метод symmetric_difference()
Аналог оператора ^.
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
sym_diff_set = set_a.symmetric_difference(set_b)
print(sym_diff_set) # {1, 2, 5, 6}
Проверка отношений между множествами
Подмножество — метод issubset()
Проверяет, являются ли все элементы одного множества частью другого. A.issubset(B) вернет True, если A является подмножеством B. Операторы < и <= также можно использовать для строгой (меньше) и нестрогой (меньше или равно) проверки.
set_a = {1, 2}
set_b = {1, 2, 3, 4}
print(set_a.issubset(set_b)) # True
print(set_a <= set_b) # True
print(set_a < set_b) # True (т.к. A не равно B)
Надмножество — метод issuperset()
Проверяет, содержит ли одно множество все элементы другого. A.issuperset(B) вернет True, если A является надмножеством B. Операторы > и >= также работают.
set_a = {1, 2, 3, 4}
set_b = {1, 2}
print(set_a.issuperset(set_b)) # True
print(set_a >= set_b) # True
print(set_a > set_b) # True (т.к. A не равно B)
Отсутствие пересечения — метод isdisjoint()
Возвращает True, если у множеств нет ни одного общего элемента.
set_a = {1, 2}
set_b = {3, 4}
set_c = {2, 5}
print(set_a.isdisjoint(set_b)) # True
print(set_a.isdisjoint(set_c)) # False (есть общий элемент 2)
Перебор элементов множества
Итерация по элементам через цикл for
Вы можете перебирать элементы множества так же, как и элементы списка, но порядок перебора не гарантирован.
my_set = {'cat', 'dog', 'fish'}
for animal in my_set:
print(animal.capitalize())
Итерация с условием или фильтрацией
Внутри цикла for можно использовать любые условия для обработки или фильтрации элементов.
numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9}
for num in numbers:
if num % 2 == 0:
print(f"Четное число: {num}")
Генераторы множеств (set comprehension)
Это компактный и эффективный способ создания множеств по аналогии с генераторами списков.
Создание множества на основе цикла
Синтаксис: {выражение for элемент in итерируемый_объект}
# Создать множество из квадратов чисел от 0 до 9
squares = {x**2 for x in range(10)}
print(squares) # {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
С условием внутри генератора
Синтаксис: {выражение for элемент in итерируемый_объект if условие}
# Создать множество из квадратов только четных чисел от 0 до 9
even_squares = {x**2 for x in range(10) if x % 2 == 0}
print(even_squares) # {0, 64, 4, 36, 16}
Преобразование между множествами и другими типами
Преобразование множества в список
Используйте list() для преобразования множества в список. Помните, что порядок элементов в итоговом списке будет произвольным.
my_set = {10, 20, 30}
my_list = list(my_set)
print(my_list) # [10, 20, 30] (порядок может отличаться)
Множество ⇄ строка
Преобразование строки в множество создает набор ее уникальных символов. Обратное преобразование можно выполнить с помощью ''.join().
unique_chars = set('abracadabra')
print(unique_chars) # {'b', 'a', 'r', 'c', 'd'}
sorted_string = "".join(sorted(list(unique_chars))) # Сортируем для предсказуемого результата
print(sorted_string) # 'abcd'
Множество ⇄ кортеж
Преобразование работает в обе стороны с помощью tuple() и set().
my_set = {1, 2, 3}
my_tuple = tuple(my_set)
print(my_tuple) # (1, 2, 3) (порядок может отличаться)
back_to_set = set(my_tuple)
print(back_to_set) # {1, 2, 3}
Множество из ключей словаря
Как было показано ранее, set() от словаря берет его ключи.
my_dict = {'a': 1, 'b': 2}
keys_set = set(my_dict.keys()) # .keys() можно опустить, результат будет тот же
print(keys_set) # {'a', 'b'}
Полезные приёмы и трюки
Удаление дубликатов из списка
Классический и самый эффективный способ: преобразовать список в множество и обратно в список.
data = [1, 2, 5, 'a', 3, 2, 1, 'a', 'b']
unique_data = list(set(data))
print(unique_data) # ['a', 1, 2, 3, 5, 'b'] (порядок не сохраняется)
Нахождение дубликатов в списке
Можно использовать множество для отслеживания уже встреченных элементов.
data = [1, 2, 5, 'a', 3, 2, 1, 'a', 'b']
seen = set()
duplicates = set()
for item in data:
if item in seen:
duplicates.add(item)
else:
seen.add(item)
print(f"Найденные дубликаты: {duplicates}") # {'a', 1, 2}
Удаление всех элементов одного списка из другого
Использование разности множеств — самый быстрый способ.
main_list = [1, 2, 3, 4, 5, 6, 7]
to_remove = [2, 4, 6, 8] # 8 нет в основном списке, это не вызовет ошибки
result = list(set(main_list) - set(to_remove))
print(result) # [1, 3, 5, 7]
Быстрое сравнение двух коллекций (без учета порядка)
Если нужно проверить, состоят ли две коллекции из одних и тех же элементов, независимо от их порядка и количества, преобразование в множества — идеальный вариант.
list1 = [1, 2, 3, 2, 1]
list2 = [3, 1, 2]
list3 = [1, 2, 4]
print(set(list1) == set(list2)) # True
print(set(list1) == set(list3)) # False
Быстрая проверка на пересечение двух структур
Метод isdisjoint() очень эффективен для проверки, есть ли у двух коллекций хотя бы один общий элемент.
user1_permissions = ['read', 'write', 'execute']
user2_permissions = {'comment', 'delete'}
user3_permissions = ('read', 'edit')
# Проверяем, есть ли хоть одно общее право
print(not set(user1_permissions).isdisjoint(user2_permissions)) # False
print(not set(user1_permissions).isdisjoint(user3_permissions)) # True
Расширенные структуры
Множества кортежей
Поскольку элементы множества должны быть неизменяемыми, вы не можете добавить в него список. Но вы можете добавить кортеж. Это полезно для хранения пар или троек уникальных значений.
# Множество координат
coordinates = {(10, 20), (30, 40), (10, 20)}
print(coordinates) # {(10, 20), (30, 40)}
# coordinates.add([50, 60]) # TypeError: unhashable type: 'list'
Замороженные множества (frozenset)
Неизменяемые множества
frozenset — это версия множества, которую нельзя изменить после создания. У него нет методов add(), remove(), update() и т. д. Однако все операции, которые возвращают новое множество (объединение, пересечение), с ним работают.
fs = frozenset([1, 2, 3, 2])
print(fs) # frozenset({1, 2, 3})
# fs.add(4) # AttributeError: 'frozenset' object has no attribute 'add'
Применение как ключи в словарях
Главное преимущество frozenset — его можно использовать в качестве ключа в словаре или элемента другого множества, так как он неизменяемый и хешируемый.
# Использование frozenset как ключа словаря
group1 = frozenset(['read', 'write'])
group2 = frozenset(['read', 'execute'])
access_levels = {
group1: 'Editor',
group2: 'Auditor'
}
print(access_levels[frozenset(['write', 'read'])]) # 'Editor' - порядок не важен
# Использование frozenset как элемента другого множества
set_of_frozensets = {frozenset([1, 2]), frozenset(['a', 'b'])}
print(set_of_frozensets)