Множества в Python (set): методы и операции
Множество в Python (set): создание, add и remove, проверка in, объединение и пересечение, удаление дубликатов из списка, frozenset и частые ошибки.
Содержание · 13 разделов
- Как создать множество
- Какие элементы можно хранить
- Добавление и удаление элементов
- Проверка вхождения: оператор in
- Операции над множествами
- Пример: что изменилось между двумя списками
- Сравнение множеств: подмножество и надмножество
- Методы множества Python: шпаргалка
- Как удалить дубликаты из списка
- Генератор множеств (set comprehension)
- Неизменяемое множество frozenset
- Обход множества циклом
- Частые ошибки
- Пустые скобки создают словарь
- Обращение по индексу
- Ожидание порядка элементов
- Изменение множества во время обхода
- update() со строкой
- 1 и True — один и тот же элемент
- Частые вопросы
- Как получить элемент множества, не удаляя его?
- Как превратить множество в список или строку?
- Как найти общие элементы нескольких списков?
- Когда выбирать множество вместо списка?
- Итог
Множество в Python (set) — неупорядоченная коллекция уникальных элементов. Короткий ответ: set([1, 2, 2, 3]) вернёт {1, 2, 3}, проверка x in s работает быстро даже на миллионе элементов, а операторы |, & и - дают объединение, пересечение и разность двух множеств.
Ниже — как создать множество, добавлять и удалять элементы, какие операции и методы есть у set, как убрать дубликаты из списка и чем set отличается от frozenset. Все примеры проверены на Python 3.12; вывод показан комментарием # →.
Как создать множество
Множество записывают в фигурных скобках или получают функцией set() из любой коллекции. Повторы отбрасываются автоматически:
numbers = {3, 1, 2, 3, 1}
print(numbers) # → {1, 2, 3}
print(type(numbers)) # → <class 'set'>
print(len(numbers)) # → 3
from_list = set([5, 5, 6, 7, 7])
print(from_list) # → {5, 6, 7}
empty = set() # пустое множество
print(empty) # → set()
print(type({})) # → <class 'dict'>
Пустые фигурные скобки {} создают словарь, а не множество. Пустое множество — только set().
set() из строки разбивает её на отдельные символы:
letters = set("банан")
print(sorted(letters)) # → ['а', 'б', 'н']
print(len(letters)) # → 3
Здесь вывод обёрнут в sorted(): порядок строк внутри множества меняется от запуска к запуску, а отсортированный список всегда одинаков.
Какие элементы можно хранить
Элементом множества может быть только хешируемый объект: число, строка, кортеж из хешируемых значений, None. Список, словарь или другое множество положить в set нельзя:
ok = {1, "два", (3, 4), None}
print(len(ok)) # → 4
bad = {[1, 2], 3} # → TypeError: unhashable type: 'list'
Это то же ограничение, что и у ключей словаря: множество устроено как хеш-таблица, только без значений.
Добавление и удаление элементов
s = {1, 2, 3}
s.add(4) # добавить один элемент
s.add(2) # уже есть — ничего не изменится
s.update([5, 6], {7}) # добавить элементы из одной или нескольких коллекций
print(s) # → {1, 2, 3, 4, 5, 6, 7}
s.remove(7) # удалить; KeyError, если элемента нет
s.discard(100) # удалить, если есть; без ошибки
print(s) # → {1, 2, 3, 4, 5, 6}
x = s.pop() # удалить и вернуть произвольный элемент
print(len(s)) # → 5
s.clear()
print(s) # → set()
Разница между remove() и discard() — только в реакции на отсутствующий элемент:
s = {1, 2}
s.remove(3) # → KeyError: 3
Если элемента может не быть, берите discard(). Метод pop() возвращает произвольный элемент — не «последний» и не «первый»: порядка у множества нет.
Проверка вхождения: оператор in
Главное практическое преимущество множества — быстрая проверка «есть ли элемент». В списке in перебирает элементы по очереди, а в множестве находит ответ по хешу за постоянное в среднем время:
banned = {"spam", "scam", "fraud"}
print("spam" in banned) # → True
print("hello" not in banned) # → True
Если в цикле вы много раз проверяете вхождение в большой список, один раз превратите его в множество: allowed = set(big_list).
Операции над множествами
Python поддерживает операции из математики множеств — как операторами, так и методами:
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b) # → {1, 2, 3, 4, 5, 6} объединение
print(a & b) # → {3, 4} пересечение
print(a - b) # → {1, 2} разность: есть в a, нет в b
print(b - a) # → {5, 6}
print(a ^ b) # → {1, 2, 5, 6} симметрическая разность
Методы делают то же самое, но принимают любой итерируемый объект, а операторы работают только с множествами:
a = {1, 2, 3, 4}
print(a.union([4, 5])) # → {1, 2, 3, 4, 5}
print(a.intersection(range(3, 10))) # → {3, 4}
print(a.difference((1, 2), [3])) # → {4}
print(a | [4, 5]) # → TypeError: unsupported operand type(s) for |: 'set' and 'list'
У каждой операции есть вариант «на месте», который меняет исходное множество:
tags = {1, 2, 3}
tags |= {4} # то же, что tags.update({4})
tags &= {2, 3, 4, 5} # оставить только общие
tags -= {3} # убрать элементы
print(tags) # → {2, 4}
Пример: что изменилось между двумя списками
Разность и пересечение удобно применять для сравнения двух наборов данных — например, списка пользователей вчера и сегодня:
old = ["anna", "oleg", "ivan"]
new = ["oleg", "ivan", "petr"]
print(sorted(set(new) - set(old))) # → ['petr'] добавились
print(sorted(set(old) - set(new))) # → ['anna'] ушли
print(sorted(set(old) & set(new))) # → ['ivan', 'oleg'] остались
Сравнение множеств: подмножество и надмножество
admins = {"anna", "oleg"}
staff = {"anna", "oleg", "ivan"}
print(admins <= staff) # → True подмножество
print(admins.issubset(staff)) # → True
print(staff >= admins) # → True надмножество
print(admins < staff) # → True строгое подмножество
print(staff < staff) # → False
print(admins.isdisjoint({"petr"})) # → True нет общих элементов
print({1, 2} == {2, 1}) # → True порядок не важен
Методы множества Python: шпаргалка
| Метод / оператор | Что делает |
|---|---|
s.add(x) | добавляет один элемент |
s.update(*others), s |= t | добавляет элементы из других коллекций |
s.remove(x) | удаляет элемент, KeyError если его нет |
s.discard(x) | удаляет элемент, если он есть |
s.pop() | удаляет и возвращает произвольный элемент |
s.clear() | очищает множество |
s.union(*others), s | t | объединение |
s.intersection(*others), s & t | пересечение |
s.difference(*others), s - t | разность |
s.symmetric_difference(t), s ^ t | элементы, которые есть только в одном из множеств |
s.issubset(t), s <= t | все элементы s есть в t |
s.issuperset(t), s >= t | все элементы t есть в s |
s.isdisjoint(t) | True, если общих элементов нет |
s.copy() | поверхностная копия |
len(s), x in s | размер, проверка вхождения |
Для intersection, difference и symmetric_difference есть версии *_update, которые меняют множество на месте. Полный справочник — в документации по типам set и frozenset.
Как удалить дубликаты из списка
Самый частый повод взять set — убрать повторы:
items = [3, 1, 3, 2, 1, 5]
print(len(set(items))) # → 4
print(list(dict.fromkeys(items))) # → [3, 1, 2, 5]
print(sorted(set(items))) # → [1, 2, 3, 5]
print(len(items) != len(set(items))) # → True есть дубликаты
list(set(items))убирает дубликаты, но теряет порядок элементов.list(dict.fromkeys(items))убирает дубликаты и сохраняет порядок первого появления: ключи словаря уникальны и идут в порядке вставки.sorted(set(items))— уникальные значения по возрастанию.
Сравнение длин списка и множества — быстрый способ проверить, есть ли в списке повторы.
Генератор множеств (set comprehension)
Множество можно собрать одной строкой по шаблону {выражение for x in коллекция}:
words = ["Python", "python", "PYTHON", "Django"]
unique = {w.lower() for w in words}
print(sorted(unique)) # → ['django', 'python']
lengths = {len(w) for w in words}
print(lengths) # → {6}
Синтаксис совпадает с генератором словаря, но без двоеточия ключ: значение.
Неизменяемое множество frozenset
frozenset — замороженная версия set: после создания его нельзя изменить, зато он хешируемый. Поэтому frozenset можно сделать ключом словаря или элементом другого множества:
fs = frozenset([1, 2, 3])
print(fs) # → frozenset({1, 2, 3})
print(fs | {4}) # → frozenset({1, 2, 3, 4})
routes = {frozenset({"Москва", "Казань"}): 820}
print(routes[frozenset({"Казань", "Москва"})]) # → 820
fs.add(4) # → AttributeError: 'frozenset' object has no attribute 'add'
Операции над множествами для frozenset работают и возвращают новый frozenset. Похожая пара «изменяемый — неизменяемый» есть у списка и кортежа.
Обход множества циклом
Множество перебирается обычным циклом for, но порядок обхода не гарантирован. Если он важен, обходите sorted(s):
for n in sorted({30, 10, 20}):
print(n)
10
20
30
Частые ошибки
Пустые скобки создают словарь
x = {}
x.add(1) # → AttributeError: 'dict' object has no attribute 'add'
Пустое множество — только set().
Обращение по индексу
s = {10, 20, 30}
s[0] # → TypeError: 'set' object is not subscriptable
У множества нет индексов и срезов. Нужен порядок и доступ по номеру — превратите в список: sorted(s)[0] вернёт наименьший элемент.
Ожидание порядка элементов
Не полагайтесь на то, в каком порядке print() выводит множество. Небольшие целые числа часто выглядят «отсортированными», но это деталь реализации; для строк порядок меняется между запусками программы. Когда порядок важен, используйте sorted() или dict.fromkeys().
Изменение множества во время обхода
s = {1, 2, 3, 4}
for n in s:
if n % 2 == 0:
s.remove(n) # → RuntimeError: Set changed size during iteration
Соберите новое множество генератором:
s = {1, 2, 3, 4}
s = {n for n in s if n % 2}
print(s) # → {1, 3}
update() со строкой
update() перебирает аргумент, поэтому строка добавится по символам. Чтобы добавить строку целиком, используйте add():
s = set()
s.update("abc")
print(sorted(s)) # → ['a', 'b', 'c']
s2 = set()
s2.add("abc")
print(s2) # → {'abc'}
1 и True — один и тот же элемент
1 == True и их хеши совпадают, поэтому {1, True, 1.0} — множество из одного элемента {1}. Учитывайте это, если храните в одном множестве числа и логические значения.
Частые вопросы
Как получить элемент множества, не удаляя его?
Индексов нет, поэтому элемент берут из итератора: next(iter(s)). Какой элемент вернётся, заранее неизвестно, — для множества из одного элемента это удобный способ достать значение.
s = {42}
print(next(iter(s))) # → 42
print(len(s)) # → 1
Как превратить множество в список или строку?
list(s) даёт список в произвольном порядке, sorted(s) — отсортированный список. Строку из строковых элементов собирают через join, предварительно отсортировав, чтобы результат не менялся между запусками.
tags = {"django", "api", "python"}
print(sorted(tags)) # → ['api', 'django', 'python']
print(", ".join(sorted(tags))) # → api, django, python
Как найти общие элементы нескольких списков?
Превратите первый список в множество и передайте остальные в intersection(): метод принимает сразу несколько коллекций, и превращать каждую в set не нужно. Так же работает union() для объединения.
a, b, c = [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 6]
print(set(a).intersection(b, c)) # → {3, 4}
Когда выбирать множество вместо списка?
Когда важны уникальность и частые проверки x in ..., а порядок и повторы не нужны. Список сохраняет порядок, допускает дубликаты и доступ по индексу. Если нужны и уникальность, и порядок, используйте ключи словаря: dict.fromkeys(items).
Итог
Множество нужно в трёх случаях: убрать дубликаты, быстро проверять вхождение через in и сравнивать наборы операциями |, &, -, ^. Помните, что у set нет порядка и индексов, а пустое множество создаётся только через set(). Остальные встроенные типы и конструкции собраны в шпаргалке по синтаксису Python; соседние коллекции — списки, словари и кортежи.
- #Python
- #set
- #Множества
- #Синтаксис
- #Коллекции