Множества (set) в Python: как создать, сравнить и убрать дубли

Set в Python хранит только уникальные значения и не помнит порядок.

Разбираем, как создать множество через фигурные скобки и set(), чем remove() отличается от discard(), как найти объединение, пересечение и разность, зачем нужен frozenset и почему порядок строк во множестве меняется при каждом запуске.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 3119 статей автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3773 экспертных мнения Подписаться на Телеграм-канал
Обложка: Множества (set) в Python: как создать, сравнить и убрать дубли

Set в Python — это множество, встроенный тип для набора уникальных значений без порядка. Создают его фигурными скобками {1, 2, 3} или функцией set(), причём повторы при создании пропадают сами: из [5, 5, 7] получится {5, 7}. Пустое множество пишут только как set(), потому что {} даёт пустой словарь.

Множествами убирают дубли, быстро проверяют, есть ли элемент в наборе, и сравнивают два набора: что общего, чего не хватает, что лишнее. Ниже каждый приём показан на коде с реальным выводом. Если вы ещё путаетесь в базовых коллекциях, сначала прочитайте, как устроены списки в Python, а про сами имена и присваивание есть отдельная статья о переменных.

CheckroiCheckroiПодборка курсов по Python569 курсов • 52 школыСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Шпаргалка по множествам в одной таблице

Рой разбирает коллекцию на Python за ноутбуком

Все примеры в таблице считаются для a = {1, 2, 3, 4} и b = {3, 4, 5, 6}.

Что нужно Оператор Метод Результат
Объединение (всё из обоих) a | b a.union(b) {1, 2, 3, 4, 5, 6}
Пересечение (только общее) a & b a.intersection(b) {3, 4}
Разность (есть в a, нет в b) a - b a.difference(b) {1, 2}
Симметрическая разность (есть только в одном) a ^ b a.symmetric_difference(b) {1, 2, 5, 6}
Подмножество a <= b a.issubset(b) False
Нет общих элементов нет a.isdisjoint(b) False
Добавить элемент нет a.add(7) изменяет a
Удалить без ошибки нет a.discard(9) изменяет a, ошибки нет

Как создать множество в Python

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

Фигурные скобки

Элементы перечисляют через запятую. Повторы Python отбрасывает сразу:

numbers = {3, 1, 2, 3, 1}
print(numbers)
print(len(numbers))
print(type(numbers))
{1, 2, 3}
3
<class 'set'>

Функция set()

Функция принимает любой перебираемый объект: список, кортеж, строку, range. Со строкой будьте внимательны: set() разберёт её на отдельные символы.

from_list = set([5, 5, 7, 7, 7])
print(from_list)

letters = set("ааббв")
print(sorted(letters))

empty = set()
print(empty, type(empty))

not_a_set = {}
print(type(not_a_set))
{5, 7}
['а', 'б', 'в']
set() <class 'set'>
<class 'dict'>

Последние две строки показывают главную ловушку новичка. Пустые фигурные скобки создают словарь, а не множество, так прямо и сказано в учебнике на docs.python.org.

Генератор множества

Это та же запись, что и генератор списка, только в фигурных скобках. Удобно, когда элементы надо сразу посчитать или отфильтровать:

squares = {n * n for n in range(-3, 4)}
print(squares)
{0, 9, 4, 1}

Из семи чисел от −3 до 3 получилось четыре квадрата: 9, 4 и 1 повторялись для отрицательных и положительных чисел.

Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Что можно положить во множество

Элементом множества может быть только хешируемый объект, то есть такой, у которого есть постоянный хеш. На практике это числа, строки, кортежи из неизменяемых значений и frozenset. Изменяемые объекты вроде списка, словаря и обычного множества внутрь не попадут.

ok = {1, 2.5, "текст", (1, 2), True}
print(len(ok))

bad = {[1, 2], 3}

Python 3.12 выведет:

4
TypeError: unhashable type: 'list'

В Python 3.14 сообщение подробнее: TypeError: cannot use 'list' as a set element (unhashable type: 'list'). Если нужен составной элемент, превратите список в кортеж: (1, 2) вместо [1, 2]. Чем кортеж отличается от списка, мы разбирали в статье про кортежи.

Вы могли заметить, что в ok пять значений, а len() вернул 4. Дело в том, что True и 1 для Python равны и дают одинаковый хеш, поэтому во множестве остаётся только первый из них:

print({True, 1, 1.0})
print({0, False})
{True}
{0}

Как добавить и удалить элементы

У множества нет индексов, поэтому a[0] не сработает. Элементы добавляют и удаляют методами:

Метод Что делает Если элемента нет
add(x) добавляет один элемент добавит; если есть, ничего не изменится
update(…) добавляет все элементы из одного или нескольких наборов то же
remove(x) удаляет элемент KeyError
discard(x) удаляет элемент молча ничего не делает
pop() удаляет и возвращает произвольный элемент KeyError на пустом множестве
clear() очищает множество целиком нет
tags = {"python"}
tags.add("set")
tags.add("python")
print(sorted(tags))

tags.update(["list", "dict"], ("tuple",))
print(sorted(tags))
['python', 'set']
['dict', 'list', 'python', 'set', 'tuple']

Повторный add("python") ошибки не дал и ничего не добавил. А вот разница между remove() и discard() важна:

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

colors.discard("black")
print(sorted(colors))

colors.remove("black")
['blue', 'green', 'red']
KeyError: 'black'

Решение: если элемента может не оказаться, берите discard(). remove() оставьте для случаев, когда отсутствие элемента означает ошибку в программе и её лучше увидеть сразу. Метод pop() по документации возвращает произвольный элемент, поэтому рассчитывать на «первый» или «последний» нельзя. У списка порядок есть, поэтому и удалять из него можно по индексу: как удалить элемент из списка в Python.

Объединение, пересечение и разность множеств

Два пересекающихся круга из цветных фишек на столе

Ради этих операций множества чаще всего и берут. Возьмём два набора чисел:

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a | b)
print(a & b)
print(a - b)
print(b - a)
print(a ^ b)
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{5, 6}
{1, 2, 5, 6}
  • |: объединение, все элементы из обоих множеств без повторов.
  • &: пересечение, только то, что есть в обоих.
  • -: разность, то, что есть в левом и нет в правом. Порядок важен: a - b и b - a дают разное.
  • ^: симметрическая разность, элементы, которые есть ровно в одном из двух.

Те же знаки у чисел означают совсем другое: | и & там побитовые операции. Таблица всех операторов есть в статье про операторы Python.

Методы или операторы

У каждой операции есть метод: union(), intersection(), difference(), symmetric_difference(). Разница одна, но заметная. Метод принимает любой перебираемый объект, а оператору нужно множество с обеих сторон:

a = {1, 2, 3}

print(a.union([3, 4], (5,)))
print(a.intersection(range(2, 10)))

print(a | [3, 4])
{1, 2, 3, 4, 5}
{2, 3}
TypeError: unsupported operand type(s) for |: 'set' and 'list'

Так задумано: в документации типа set сказано, что методы принимают любой итерируемый аргумент, а операторам нужны множества. Если данные пришли списком, вызовите метод или оберните список в set().

Изменить множество на месте

Операторы |=, &=, -= и ^= не создают новое множество, а меняют левое. Это же делают методы update(), intersection_update(), difference_update() и symmetric_difference_update().

a = {1, 2, 3, 4}
b = {3, 4, 5}

a -= b
print(a)

a |= {7}
print(a)

a &= {1, 7, 9}
print(a)
{1, 2}
{1, 2, 7}
{1, 7}

Если вы привыкли к спискам, add() работает как append(), а update() как extend(): добавляет всё из другого набора. Как эти методы ведут себя у списков, мы разбирали в статье как добавить элемент в список Python.

Как проверить элемент и сравнить два множества

Проверка in выглядит так же, как у списка:

week = {"пн", "вт", "ср"}
print("вт" in week)
print("сб" not in week)
True
True

Разница в том, как Python ищет. Список он перебирает с начала до совпадения, примерно как вы сделали бы это циклом while с проверкой каждого элемента.

Множество устроено как хеш-таблица: Python считает хеш значения и сразу смотрит в нужную ячейку. На наборе из нескольких элементов разницы не видно. А на сотнях тысяч поиск в списке растёт вместе с длиной, в множестве же в среднем занимает одинаковое время. Поэтому проверку на вхождение документация Python называет одним из основных применений множеств.

Для сравнения наборов есть пять операций:

small = {1, 2}
big = {1, 2, 3}

print(small <= big, small.issubset(big))
print(small < big)
print(big < big, big <= big)
print(big >= small, big.issuperset(small))
print(small.isdisjoint({8, 9}))
print({1, 2} == {2, 1})
True True
True
False True
True True
True
True
  • <= или issubset(): все элементы левого есть в правом.
  • <: то же, но множества не равны. Поэтому big < big даёт False.
  • >= или issuperset(): левое содержит всё из правого.
  • isdisjoint(): True, если общих элементов нет вообще.
  • ==: одинаковый состав, порядок записи не важен.
Ваня БуявецКанал основателя Checkroi Вани БуявцаЗабирайте промпты и обучение по нейросетям в моём Телеграм-каналеБольше 3 700 человек уже применяют Claude Code, ChatGPT и другие нейросети в работе, учёбе, бизнесе и жизниПерейти в канал

frozenset: неизменяемое множество

frozenset умеет всё, что не меняет набор: операции, сравнения, in, len(). Методов add(), remove() и других изменяющих у него нет. Зато он хешируемый, и его можно сделать ключом словаря или элементом другого множества. Обычный set так не может, это прямо сказано в описании frozenset.

frozen = frozenset([1, 2, 2, 3])
print(frozen)

routes = {frozenset({"Москва", "Казань"}): 820}
print(routes[frozenset({"Казань", "Москва"})])

print(frozen | {4})

frozen.add(4)
frozenset({1, 2, 3})
820
frozenset({1, 2, 3, 4})
AttributeError: 'frozenset' object has no attribute 'add'

Пример с маршрутами показывает, зачем это нужно. Ключ frozenset({"Москва", "Казань"}) не зависит от порядка городов, поэтому значение находится по паре в любом порядке, и вторую запись «Казань — Москва» заводить не нужно. Операции вроде frozen | {4} работают, но возвращают новый объект, а исходный остаётся прежним.

Где множества пригодятся на практике

Рой радуется, что дубли исчезли

Три задачи встречаются постоянно.

Убрать дубли из списка. Если порядок не важен, хватит set(список). Если важен, используйте словарь: с Python 3.7 он гарантированно хранит порядок добавления, и dict.fromkeys() оставит первое появление каждого значения.

emails = ["a@mail.ru", "b@mail.ru", "a@mail.ru", "c@mail.ru", "b@mail.ru"]

print(len(set(emails)))

unique_in_order = list(dict.fromkeys(emails))
print(unique_in_order)
3
['a@mail.ru', 'b@mail.ru', 'c@mail.ru']

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

yesterday = {"anna", "boris", "vera"}
today = {"boris", "vera", "gleb"}

print("новые:", sorted(today - yesterday))
print("ушли:", sorted(yesterday - today))
print("оба дня:", sorted(yesterday & today))
новые: ['gleb']
ушли: ['anna']
оба дня: ['boris', 'vera']

Быстро проверять «было или нет». Список уже обработанных ID, стоп-слов или посещённых страниц лучше хранить во множестве: каждая проверка in не перебирает весь набор. В базах данных ту же задачу решает DISTINCT в SQL, а потренироваться на множествах можно в задачах по Python для начинающих.

Частые ошибки с множествами

  1. Создали пустое множество через {}. Получился словарь, и add() падает с AttributeError: 'dict' object has no attribute 'add'. Решение: set().
  2. Положили список. TypeError: unhashable type: 'list'. Решение: кортеж вместо списка или frozenset вместо вложенного множества.
  3. Ждут порядок. Множество не помнит, в каком порядке добавляли элементы, и индексов у него нет. Если порядок нужен, сортируйте: sorted(s) вернёт список.
  4. Удалили несуществующий элемент через remove(). Получили KeyError. Решение: discard() или проверка if x in s.
  5. Меняли множество внутри цикла по нему. Python остановит программу с ошибкой RuntimeError: Set changed size during iteration. Решение ниже.

Так делать нельзя:

nums = {1, 2, 3, 4}
for n in nums:
    if n % 2 == 0:
        nums.discard(n)
RuntimeError: Set changed size during iteration

Решение: соберите новое множество генератором и присвойте его той же переменной.

nums = {1, 2, 3, 4}
nums = {n for n in nums if n % 2 != 0}
print(nums)
{1, 3}

Почему порядок строк меняется от запуска к запуску

Целые числа в небольших множествах часто выводятся по возрастанию, и легко решить, что множество их сортирует. Со строками видно, что это не так. Запустите одну строку кода несколько раз:

print({"кот", "пёс", "ёж"})
{'кот', 'ёж', 'пёс'}
{'ёж', 'пёс', 'кот'}
{'пёс', 'кот', 'ёж'}

Хеши строк Python при каждом запуске «солит» случайным значением, это включено по умолчанию и защищает от атак на хеш-таблицы. Порядок элементов во множестве зависит от хешей, поэтому меняется вместе с ними. Если тест сравнивает вывод множества строкой, он будет то проходить, то падать. Сравнивайте сами множества через == или выводите sorted(), как в примерах этой статьи.

Где освоить Python системно

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

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
Профессия «Python-разработчик»
Перейти на сайт курса
SkillboxSkillbox157 335 ₽5987 ₽/мес.10 месяцевОбзор курса
Fullstack-разработчик на Python
Перейти на сайт курса
НетологияНетология137 000 ₽7125 ₽/мес.21 месяцОбзор курса
ДО Профессия Python-разработчик
Перейти на сайт курса
GeekBrainsGeekBrains149 001 ₽3167 ₽/мес.10 месяцевОбзор курса
Автоматизированное тестирование на Python
Перейти на сайт курса
SkillboxSkillbox122 226 ₽4108 ₽/мес.9 месяцевОбзор курса
Профессия Python-разработчик
Перейти на сайт курса
SkillFactorySkillFactory140 040 ₽3890 ₽/мес.10 месяцевОбзор курса

Больше программ — в полном каталоге курсов по Python

Часто задаваемые вопросы

Что такое set в Python?

Это встроенный тип «множество»: набор уникальных значений без порядка и без индексов. Повторы при создании отбрасываются, а проверка «есть ли элемент» через in не перебирает весь набор.

Как создать пустое множество в Python?

Только через set(). Пустые фигурные скобки {} создают словарь, и вызов add() на нём падает с AttributeError.

Чем множество отличается от списка?

В списке есть порядок, индексы и повторы, а во множестве ничего этого нет. Зато множество убирает дубли само и умеет объединение, пересечение и разность. Подробно про списки — в статье о списках в Python.

Что такое frozenset и зачем он нужен?

Это неизменяемое множество: у него нет add(), remove() и других методов, которые меняют набор. Зато frozenset хешируемый, поэтому его можно сделать ключом словаря или положить в другое множество.

Чем remove() отличается от discard()?

Оба удаляют элемент. Если элемента нет, remove() выбрасывает KeyError, а discard() молча ничего не делает.

Почему множество выводится в другом порядке?

Множество не хранит порядок добавления. Порядок вывода зависит от хешей элементов, а хеши строк меняются при каждом запуске программы. Нужен стабильный порядок — используйте sorted().

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!