Урок курса

Множество: создание и проверка принадлежности

Python для новичков

В прошлых уроках мы разобрали списки и словари. Список хранит элементы в порядке и допускает дубликаты, а словарь хранит пары ключ–значение. Сегодня познакомимся с новой коллекцией — множеством. Оно решает конкретную задачу: хранить только уникальные значения, не заботясь о порядке.

Что такое множество и зачем оно нужно

Представь, что ты собираешь список городов, которые посетили пользователи. Один и тот же пользователь может прислать «Москва» трижды. Если хранить города в обычном списке, дубликаты накапливаются. Множество решает это автоматически: оно просто не даёт одному и тому же значению попасть внутрь дважды.

Множество (тип set) — это встроенная коллекция Python, которая хранит только уникальные элементы. Два ключевых свойства отличают его от списка:

  • Нет дубликатов. Если добавить одно и то же значение несколько раз, в множестве оно будет ровно один раз.
  • Нет порядка. Множество не запоминает, в каком порядке добавлялись элементы. Поэтому к элементам нельзя обратиться по индексу — конструкция s[0] вызовет ошибку.

Сравни со списком:

cities_list = ["Москва", "Питер", "Москва", "Казань", "Питер"]
print(cities_list)  # ['Москва', 'Питер', 'Москва', 'Казань', 'Питер']
print(len(cities_list))  # 5 — все пять, включая дубли

Тот же набор данных, но через множество:

cities_set = {"Москва", "Питер", "Москва", "Казань", "Питер"}
print(cities_set)   # {'Москва', 'Питер', 'Казань'} — порядок произвольный
print(len(cities_set))  # 3 — только уникальные

Порядок вывода может меняться от запуска к запуску — это нормально. Множество не обязано выдавать элементы в том же порядке, в котором они были переданы.

Когда множество уместно, а когда нет:

  • Нужно хранить уникальные значения и быстро проверять, есть ли что-то в коллекции — множество подходит.
  • Нужно хранить порядок, дубликаты или обращаться к элементам по позиции — нужен список.

Свойство уникальности — не просто техническая деталь, а главная причина, зачем вообще использовать set. Всё остальное в уроке строится вокруг этой идеи.

Создание множества: литерал, конструктор и ловушка с {}

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

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

genres = {"рок", "джаз", "поп"}
print(genres)   # {'рок', 'джаз', 'поп'} — порядок может отличаться
print(type(genres))  # <class 'set'>

Конструктор set() — когда нужно создать пустое множество или преобразовать уже существующую последовательность:

empty = set()          # пустое множество
print(empty)           # set()
print(type(empty))     # <class 'set'>

tags = set(["python", "data", "python", "code"])  # из списка с дублями
print(tags)  # {'python', 'data', 'code'}

Во втором примере set() принял список как аргумент и убрал повтор "python" автоматически.

Ловушка с пустыми {}

Есть момент, на котором спотыкаются почти все, кто знакомится с множеством после словарей. Пустые фигурные скобки {} в Python — это словарь, а не множество:

wrong = {}
print(type(wrong))  # <class 'dict'>  — словарь!

correct = set()
print(type(correct))  # <class 'set'>  — то что нужно

Если написать s = {} и потом попробовать вызвать s.add("что-то"), Python выдаст AttributeError — у словаря нет метода add(). Ошибка не всегда очевидна, потому что на вид {} выглядит как разумная запись.

Правило простое: пустое множество — только через set(). Литерал {...} работает только тогда, когда внутри есть хотя бы один элемент.

Изменение множества: add(), discard() и безопасное удаление

Множество не статично: после создания в него можно добавлять и удалять элементы. Для этого есть два метода — add() и discard().

add() добавляет элемент в множество. Если элемент уже есть, ничего не происходит — никакой ошибки, множество просто остаётся прежним:

visited = {"Москва", "Казань"}
visited.add("Питер")
print(visited)  # {'Москва', 'Казань', 'Питер'}

visited.add("Москва")  # уже есть
print(visited)  # {'Москва', 'Казань', 'Питер'} — без изменений

Вот здесь уникальность работает на практике: add() не создаёт дубликат, даже если вызвать его несколько раз с одним и тем же значением.

discard() удаляет элемент из множества. Ключевое слово — «безопасно»: если элемента нет, метод молча ничего не делает:

visited.discard("Казань")
print(visited)  # {'Москва', 'Питер'}

visited.discard("Новосибирск")  # такого нет в множестве
print(visited)  # {'Москва', 'Питер'} — никакой ошибки

Такое поведение принципиально отличает discard() от remove(). Метод remove() есть и у множества, и у списка — но при попытке удалить несуществующий элемент он бросает исключение:

visited.remove("Новосибирск")  # KeyError: 'Новосибирск'

Это не значит, что remove() плохой — просто у него другая задача. Если хочешь гарантированно получить ошибку при отсутствии элемента (например, чтобы отловить логический баг), используй remove(). Если хочешь просто «убрать, если есть» — используй discard().

Аналогия со списком помогает быстро запомнить разницу: append() добавляет в конец списка, add() добавляет в множество. remove() у списка тоже падает с ошибкой при отсутствии элемента — discard() это исправляет специально для множеств.

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

wishlist = set()
wishlist.add("Берлин")
wishlist.add("Токио")
wishlist.add("Берлин")   # повтор — игнорируется
wishlist.discard("Токио") # передумал
wishlist.discard("Рим")   # не было — не падает
print(wishlist)  # {'Берлин'}

Весь блок работает без ошибок, даже если данные приходят непоследовательно. Именно за это и ценят discard() в сценариях, где наличие элемента заранее неизвестно.

Проверка принадлежности оператором in и получение уникальных элементов из списка

Два самых частых сценария работы с множеством — проверить, есть ли что-то внутри, и убрать дубликаты из списка. Оба делаются в одну строку.

Оператор in

Для множества in работает так же, как для списка: возвращает True или False. Синтаксис одинаковый, но есть практическое преимущество: множество устроено иначе внутри — детали этого изучают позже, но на практике это означает значительно более быструю проверку, чем у списка, даже при большом числе элементов.

Пример с городами:

visited = {"Москва", "Питер", "Казань"}

print("Москва" in visited)      # True
print("Новосибирск" in visited) # False
print("Питер" not in visited)   # False

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

subscribed = {"alice@mail.ru", "bob@mail.ru"}

user = "carol@mail.ru"
if user not in subscribed:
    subscribed.add(user)
    print(f"{user} добавлен в рассылку")
else:
    print(f"{user} уже подписан")

Здесь in и add() работают вместе: проверяем членство — добавляем только тех, кого ещё нет.

Паттерн set(список) для дедупликации

Если есть список с повторами и нужны только уникальные значения — конвертируй его в множество:

cities = ["Москва", "Питер", "Москва", "Казань", "Питер", "Казань"]
unique_cities = set(cities)
print(unique_cities)  # {'Москва', 'Питер', 'Казань'} — порядок произвольный

Если после дедупликации нужен список (например, чтобы отсортировать или передать в функцию, которая ждёт именно список):

unique_sorted = sorted(set(cities))
print(unique_sorted)  # ['Казань', 'Москва', 'Питер']

sorted() принимает любую коллекцию, поэтому его можно применить прямо к множеству — результат будет списком, отсортированным по алфавиту.

Полный мини-сценарий: собираем теги из нескольких источников, убираем дубликаты и проверяем, есть ли нужный тег:

raw_tags = ["python", "data", "python", "ml", "data", "python"]
tags = set(raw_tags)
print(tags)  # {'python', 'data', 'ml'}

print("ml" in tags)      # True
print("deep" in tags)    # False

Один вызов set() — и список из шести элементов превращается в три уникальных. После этого in позволяет быстро проверить наличие любого тега.

Задача 1

Программист написал:

s = {}
print(type(s))

Какой тип выведет Python?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 2

Что выведет следующий код? Напишите одно число.

nums = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3}
print(len(nums))
Без регистрации · результат не сохраняется

Задача 3

Посмотрите на код:

tags = {'python', 'код', 'урок'}
print('код' in tags)
print('java' in tags)

Что выведет программа?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 4

Программист хочет вывести количество уникальных товаров из списка с дубликатами:

shop = ['хлеб', 'молоко', 'хлеб', 'сыр']

Какой код решает задачу правильно?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 5

Посмотрите на код:

visited = {'Москва', 'Берлин', 'Париж'}
visited.discard('Берлин')
visited.discard('Токио')
print(len(visited))

Что выведет программа?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 6

Интересы участников

В начале программы в файле уже должно быть создано пустое множество interests = set().

Затем добавьте в него по очереди темы python, math, python.

После этого программа читает одну строку из ввода — тему участника.
Если эта тема уже есть в множестве, выведите Да, иначе — Нет.

Повторное добавление одинаковой темы не должно увеличивать число элементов в множестве.

РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Задача 7

Безопасно удали два имени

В программе уже задано множество subscribers = {'anna', 'boris', 'vika'}.

Прочитайте две строки — два имени, которые нужно удалить. Удалите каждое имя из множества методом discard().

Выведите результат строго в двух строках:

  1. в первой строке — количество оставшихся подписчиков;
  2. во второй строке — оставшиеся имена в алфавитном порядке, разделённые одним пробелом.

Выводите имена без фигурных скобок, кавычек и запятых. Чтобы получить стабильный порядок, используйте sorted(subscribers).

Если какого-либо имени в множестве нет, программа всё равно должна завершиться без ошибки.

РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Задача 8

Проверь повторяющиеся слова в строке

Программа получает одну строку с названиями слов через пробел.

Нужно:

  1. превратить ввод в список слов;
  2. получить из него множество уникальных слов;
  3. вывести сначала количество уникальных слов;
  4. затем вывести:
  • Есть повторения, если в исходной строке были одинаковые слова;
  • Повторов нет, если все слова были разными.

Повторения определяйте только сравнением количества слов до и после преобразования в множество.

РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Задачи к уроку

Все задачи курса →

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку