Перейти к содержимому
Теория · Словари и множества · урок 7 из 11

Множества

Вернёмся к прилавку. Рядом с табличкой цен висит вторая, «в наличии»: на ней только названия, без цен. Каждое название написано один раз, потому что писать pear дважды незачем.

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

Python
tags = ["python", "web", "python", "api", "web"]
unique_tags = set(tags)
print(len(tags), len(unique_tags))  # 5 3
print(sorted(unique_tags))  # ['api', 'python', 'web']
print("api" in unique_tags)  # True

Давайте разберём по строкам:

  1. set(tags) убирает повторы: из пяти тегов остаются три разных.
  2. print(len(tags), len(unique_tags)) печатает 5 3.
  3. sorted(unique_tags) возвращает список по алфавиту, так множество печатают в предсказуемом порядке.
  4. "api" in unique_tags проверяет, есть ли элемент, и даёт True.

Без порядка и без индексов

Литерал множества перечисляет элементы в фигурных скобках: {1, 2, 3}. Порядка у элементов нет: print может показать их в любом порядке, поэтому для вывода множество оборачивают в sorted. Индексов и срезов тоже нет: unique_tags[0] вызывает TypeError: 'set' object is not subscriptable.

Здесь табличка «в наличии» перестаёт быть точной картинкой. Строки на ней идут сверху вниз, а у множества нет ни первого элемента, ни последнего.

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

Методы add и remove

Метод add добавляет элемент, а повтор ничего не меняет. Метод remove удаляет элемент:

Python
team = {"ann", "bob"}
team.add("eve")
team.add("ann")
team.remove("bob")
print(sorted(team))  # ['ann', 'eve']

Разберём по строкам:

  1. team.add("eve") добавляет нового игрока.
  2. team.add("ann") ничего не меняет: "ann" уже в множестве.
  3. team.remove("bob") удаляет "bob", и остаются ['ann', 'eve'].

Если элемента нет, remove останавливает программу ошибкой KeyError, как чтение отсутствующего ключа словаря.

Хешируемые элементы

Множество устроено как ключи словаря без значений. Отсюда два следствия:

  • элементы должны быть хешируемыми, поэтому список в множество не положить: {[1, 2]} вызывает TypeError: unhashable type: 'list';
  • как и словарь, множество находит элемент по хешу, без перебора, поэтому in здесь намного быстрее, чем в длинном списке.

Неизменяемый вариант множества — тип frozenset. Он хешируемый, поэтому годится как ключ словаря или элемент другого множества.

Этот вопрос часто задают на собеседовании: «Чем set отличается от frozenset?» Ответ: set можно менять через add и remove, а frozenset после создания не меняется. Зато frozenset хешируемый, а set — нет.

Операции

Два множества можно сравнить тремя операторами. Каждая операция возвращает новое множество, а исходные не меняются:

Python
team_a = {"ann", "bob", "eve"}
team_b = {"bob", "kim"}
print(sorted(team_a | team_b))  # ['ann', 'bob', 'eve', 'kim']
print(sorted(team_a & team_b))  # ['bob']
print(sorted(team_a - team_b))  # ['ann', 'eve']
print(sorted(team_b - team_a))  # ['kim']

Объединение |

Оператор | даёт объединение — все элементы обоих множеств, каждый по одному разу. "bob" играет в обеих командах, но в team_a | team_b он встречается один раз.

Пересечение &

Оператор & даёт пересечение — общие элементы. Здесь team_a & team_b — игроки обеих команд, то есть только "bob".

Разность -

Оператор - даёт разность — элементы первого множества, которых нет во втором. team_a - team_b — те, кто играет только в первой команде.

Разность зависит от порядка операндов: team_b - team_a даёт ['kim']. Объединение и пересечение от порядка не зависят.

Частая ошибка

Пустое множество записали как {}:

Python
seen = {}
seen.add("ann")  # ✗
TEXT
AttributeError: 'dict' object has no attribute 'add'

Пустые фигурные скобки создают словарь, а у словаря нет метода add. Пустое множество создаётся только вызовом set().

Главное

  • Множество хранит каждое значение один раз, и set(список) убирает повторы.
  • У множества нет порядка и индексов, поэтому для вывода его оборачивают в sorted.
  • Элементы множества должны быть хешируемыми, а frozenset — неизменяемое и хешируемое множество.
  • |, & и - дают объединение, пересечение и разность; пустое множество создаёт только set().

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом