Тип set в языке программирования Python
Основные возможности и способы работы с set
Как создать пустое множество?
Для создания пустого множества применяется конструктор set(). Использование фигурных скобок {} создает словарь, а не множество.
empty_set = set()
print(type(empty_set)) # Python set (тип set в python)
Множество можно создать из любого итерируемого объекта, например из списка или строки.
set_from_list = set([1, 2, 3])
set_from_string = set('hello') # {'h', 'e', 'l', 'o'}практические задания по python (практические задания по python)
TypeError возникает при попытке указать в качестве элемента изменяемый тип, например список или словарь.
s = {[1, 2]} # TypeError: unhashable type: 'list'уроки python с заданиями (уроки python с заданиями)
Как добавить один элемент в множество?
Метод add() добавляет один элемент, если его еще нет.
s = {1, 2, 3}
s.add(4)
s.add(2) # ничего не произойдет, так как 2 уже есть
print(s) # {1, 2, 3, 4}
Метод update() добавляет сразу несколько элементов из итерируемого объекта.
s = {1, 2}
s.update([3, 4, 5])
print(s) # {1, 2, 3, 4, 5}
Попытка добавить изменяемый объект (например список) вызовет TypeError.
s = set()
s.add([1, 2]) # TypeError
Как удалить элемент из множества?
Метод remove() удаляет элемент, вызывая KeyError, если его нет. Для безопасного удаления используется discard().
s = {1, 2, 3}
s.remove(2)
s.discard(10) # ошибки нет, элемент просто не удален
print(s) # {1, 3}
pop() удаляет и возвращает произвольный элемент. Если множество пусто, возникает KeyError.
s = {10, 20, 30}
elem = s.pop()
print(elem, s) # например 10 {20, 30}
Итерация по множеству с одновременным удалением элементов (кроме clear) может привести к RuntimeError: Set changed size during iteration.
s = {1, 2, 3}
for x in s:
s.remove(x) # RuntimeError
Как проверить наличие элемента в множестве?
Оператор in выполняет проверку за O(1) в среднем.
s = {1, 2, 3}
print(2 in s) # True
print(4 in s) # False
Также можно проверить отсутствие с помощью not in.
if 5 not in s:
print('5 отсутствует')
Память: не стоит злоупотреблять проверками в цикле для изменяемого множества, но это редкость.
Как объединить два множества?
Метод union() или оператор | возвращает новое множество, содержащее все элементы из обеих.
a = {1, 2, 3}
b = {3, 4, 5}
c = a.union(b) # {1, 2, 3, 4, 5}
d = a | b # то же самое
Метод update() изменяет исходное множество, добавляя элементы из другого.
a.update(b) # a теперь {1, 2, 3, 4, 5}
Аргументы union и update могут быть любыми итерируемыми объектами, но добавление нехэшируемых элементов вызовет ошибку.
Как найти пересечение множеств?
Используется метод intersection() или оператор &.
a = {1, 2, 3}
b = {2, 3, 4}
c = a.intersection(b) # {2, 3}
d = a & b # {2, 3}
Метод intersection_update() изменяет исходное множество, оставляя только общие элементы.
a.intersection_update(b) # a теперь {2, 3}
Пересечение с пустым множеством всегда пусто.
Как найти разность и симметрическую разность?
Разность (элементы из первого, но не из второго) difference() или -. Симметрическая разность (элементы, входящие в одно из множеств, но не в оба) symmetric_difference() или ^.
a = {1, 2, 3}
b = {2, 3, 4}
print(a - b) # {1}
print(a ^ b) # {1, 4}
Для изменения на месте существуют difference_update() и symmetric_difference_update().
a ^= b # a становится {1, 4}
Симметрическая разность ассоциативна, но не коммутативна? На самом деле она коммутативна: a ^ b == b ^ a.
Как проверить, является ли одно множество подмножеством другого?
Методы issubset() (<=) и issuperset() (>=) проверяют включение.
a = {1, 2}
b = {1, 2, 3}
print(a.issubset(b)) # True
print(b.issuperset(a)) # True
print(a < b) # строгое подмножество (a <= b и a != b)
isdisjoint() проверяет, не имеют ли множества общих элементов.
c = {4, 5}
print(a.isdisjoint(c)) # True (общих элементов нет)
При сравнении пустое множество является подмножеством любого множества.
Зачем нужен frozenset?
frozenset - неизменяемая версия множества. Его можно использовать как ключ словаря или элемент другого множества.
fs = frozenset([1, 2, 3])
d = {fs: 'value'}
print(d[fs]) # 'value'
frozenset поддерживает все немодифицирующие операции (union, intersection, …), возвращая frozenset.
fs2 = frozenset([2, 3, 4])
fs3 = fs.union(fs2) # frozenset({1, 2, 3, 4})
Нельзя изменить frozenset после создания; отсутствуют методы add, remove и т.д.
Как убрать дубликаты из списка с помощью set?
Преобразование списка в множество удаляет дубликаты, затем обратно в список (порядок не сохраняется).
original = [1, 2, 2, 3, 1]
unique = list(set(original))
print(unique) # [1, 2, 3] (порядок может быть другим)
Для сохранения порядка (Python 3.7+ dict сохраняет порядок) можно использовать dict.fromkeys().
unique_ordered = list(dict.fromkeys(original))
print(unique_ordered) # [1, 2, 3] (порядок сохранен)
Если элементы списка нехэшируемы (например вложенные списки), set применить нельзя. Тогда нужна ручная проверка.
Специфические особенности работы с set
Множества в Python неупорядочены. Порядок итерации может меняться между запусками программы (до Python 3.7 была случайность, сейчас фиксирован, но не гарантирован).
Для работы с упорядоченными уникальными элементами используется OrderedSet (из модуля collections нет стандартного) или комбинация dict.
Изменять множество во время итерации по нему нельзя - создайте копию: for x in s.copy(): ...
Дополнительные примеры с set
Пример 1. Преобразование списка с дубликатами и сохранение порядка
Использование dict.fromkeys() для удаления дубликатов с сохранением первоначального порядка.
data = [3, 1, 2, 1, 3, 4]
unique = list(dict.fromkeys(data))
print(unique)
[3, 1, 2, 4]
Пример 2. Frozenset как ключ словаря
Неизменяемое множество может служить ключом, что полезно для кэширования или группировки.
fs1 = frozenset([1, 2])
fs2 = frozenset([3, 4])
cache = {fs1: 'первая группа', fs2: 'вторая группа'}
print(cache[frozenset([1, 2])])
первая группа
Пример 3. Операции над множествами с выводом результатов
Демонстрируются все основные операции.
a = set('abracadabra')
b = set('alacazam')
print('a:', a)
print('b:', b)
print('a | b:', a | b)
print('a & b:', a & b)
print('a - b:', a - b)
print('a ^ b:', a ^ b)
a: {'b', 'a', 'c', 'r', 'd'}
b: {'l', 'a', 'c', 'z', 'm'}
a | b: {'b', 'a', 'c', 'r', 'd', 'l', 'z', 'm'}
a & b: {'a', 'c'}
a - b: {'b', 'r', 'd'}
a ^ b: {'b', 'r', 'd', 'l', 'z', 'm'}
Пример 4. Проверка подмножества и непересечения
x = {1, 2, 3}
y = {1, 2}
z = {4}
print(y.issubset(x)) # True
print(x.issuperset(y)) # True
print(x.isdisjoint(z)) # True
True True True
Пример 5. Set comprehension (генератор множеств)
Создание множества квадратов чисел от 0 до 9.
squares = {x**2 for x in range(10)}
print(squares)
{0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
Пример 6. Быстрая проверка на вхождение (производительность)
Сравнение времени проверки для списка и множества.
import time
lst = list(range(100000))
st = set(range(100000))
start = time.perf_counter()
for _ in range(1000):
99999 in lst
print('list:', time.perf_counter() - start)
start = time.perf_counter()
for _ in range(1000):
99999 in st
print('set:', time.perf_counter() - start)
list: ~0.025 сек set: ~0.0002 сек
Пример 7. Ошибка при итерации с удалением и способ обхода
s = {1, 2, 3, 4}
# неверно:
# for x in s:
# if x % 2 == 0:
# s.remove(x)
# верно:
for x in s.copy():
if x % 2 == 0:
s.remove(x)
print(s)
{1, 3}
Пример 8. Вложенные frozenset
Множество из frozenset возможно, так как frozenset хэшируем.
fs1 = frozenset([1, 2])
fs2 = frozenset([3, 4])
set_of_frozensets = {fs1, fs2}
print(set_of_frozensets)
{frozenset({1, 2}), frozenset({3, 4})}
Пример 9. Преобразование строки в множество символов и обратно
text = 'программирование'
unique_chars = set(text)
print(unique_chars)
sorted_chars = ''.join(sorted(unique_chars))
print(sorted_chars)
{'м', 'р', 'о', 'г', 'и', 'а', 'в', 'п', 'н', 'е'}
авгимнопре
Пример 10. Использование set для поиска общих элементов в нескольких последовательностях
list1 = [1, 2, 3, 4]
list2 = [3, 4, 5, 6]
list3 = [4, 5, 6, 7]
common = set(list1) & set(list2) & set(list3)
print(common)
{4}