Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 8 из 20 Начальный 15 мин 100 XP

Разность и симметричная разность множеств в Python

Разность отвечает на вопрос «что есть у первого и чего нет у второго», симметричная — «что есть ровно у одного из двух». Операторы - и ^, методы difference и symmetric_difference и почему порядок множеств важен.

Редакция Питоники

Объединение в уроке 6 собирало всё вместе, пересечение в уроке 7 — только общее. Остались два вопроса про пару множеств: что есть у одного и чего не хватает, и что есть ровно у одного из двух. Первое — разность, второе — симметричная разность. Обе постоянно нужны на данных: приглашённые гости минус пришедшие — кто не явился; все задачи минус сделанные — что осталось.

Разность: что есть у первого и нет у второго

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

приглашённые минус пришедшие
invited = {'Анна', 'Борис', 'Вера', 'Глеб'}
came = {'Борис', 'Вера', 'Даша'}

no_show = invited - came
print(sorted(no_show))
print(len(no_show))
Вывод
['Анна', 'Глеб']
2

Множества печатаются через sorted — состав определён, а порядок вывода мы задаём сами. Тот же результат даёт метод difference — разница только в написании. Сравним их напрямую:

метод difference
invited = {'Анна', 'Борис', 'Вера', 'Глеб'}
came = {'Борис', 'Вера', 'Даша'}

print(sorted(invited.difference(came)))
print(invited.difference(came) == invited - came)
Вывод
['Анна', 'Глеб']
True

Порядок важен: a - b не равно b - a

Разность несимметрична: из первого множества вычитается второе, поэтому a - b и b - a — два разных результата. В арифметике 10 - 4 и 4 - 10 тоже разные — с множествами то же самое: поменять операнды местами нельзя без смены смысла. Возьмём план на день и уже сделанное:

два направления вычитания
planned = {'созвон', 'отчёт', 'ревью', 'пароль'}
done = {'отчёт', 'пароль'}

print(sorted(planned - done))
print(sorted(done - planned))
print(planned - done == done - planned)
Вывод
['ревью', 'созвон']
[]
False

Из плана ушли сделанные задачи — остались две нерешённые. В обратную сторону разность пуста: всё, что сделано, было и в плане. Это два разных вопроса: что осталось и что сделано сверх плана.

Симметричная разность: только у одного из двух

Обратный вопрос: кто есть ровно в одном из двух списков и не совпадает. В понедельник и в пятницу дежурили так:

циркумфлекс и метод
monday = {'Анна', 'Борис', 'Вера'}
friday = {'Борис', 'Вера', 'Глеб'}

print(sorted(monday ^ friday))
print(sorted(monday.symmetric_difference(friday)))
print(monday ^ friday == friday ^ monday)
Вывод
['Анна', 'Глеб']
['Анна', 'Глеб']
True

Симметричная разность собрала Анну и Глеба: Анна дежурила только в понедельник, Глеб — только в пятницу. Борис и Вера были в обоих списках, поэтому в результат не попали. В отличие от обычной разности, симметричная коммутативна: a ^ b и b ^ a — одно и то же, порядок операндов не имеет значения. По смыслу это «или-или»: элемент попадает в результат, если есть в первом ИЛИ во втором, но не в обоих сразу.

Метод принимает список, оператор — нет

Операторы - и ^ требуют множества с обеих сторон: подсовать список нельзя. Методы же принимают любую коллекцию — список, кортеж, range. Это продолжение знакомой темы: одиночная проверка in из урока 5 тоже принимала что угодно:

методу годится список
stock = {'чай', 'кофе', 'сахар'}
order = ['чай', 'мёд']

print(sorted(stock.difference(order)))
Вывод
['кофе', 'сахар']
оператору список не годится
stock = {'чай', 'кофе', 'сахар'}
order = ['чай', 'мёд']

print(stock - order)
Вывод
Traceback (most recent call last):
  File "app.py", line 4, in <module>
    print(stock - order)
          ~~~~~~^~~~~~~
TypeError: unsupported operand type(s) for -: 'set' and 'list'
Программа падает на вычитании: оператор - требует множества с обеих сторон, а справа список. Метод difference из блока выше с теми же данными работает.

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

Вычесть на месте: -= и методы с update

Когда результат нужен прямо в первом множестве, работают изменяющие варианты: a -= b вычитает b из a, a ^= b оставляет в a только то, что есть ровно в одном из двух. Методы называются difference_update и symmetric_difference_update:

вычитание на месте
cart = {'хлеб', 'молоко', 'сыр', 'чай'}
sold_out = {'сыр', 'чай'}

cart -= sold_out
print(sorted(cart))

tags = {'новое', 'sale', 'top'}
fresh = {'sale', 'осень'}

tags ^= fresh
print(sorted(tags))
Вывод
['молоко', 'хлеб']
['top', 'новое', 'осень']

Корзина потеряла распроданные позиции, теги — общие с новой партией. Обратите внимание на сортировку: латинское top стоит перед кириллицей — сравниваются коды символов, а не языки. Методы с update меняют множество и возвращают None, поэтому пишутся отдельной строкой без присваивания: tags.symmetric_difference_update(fresh).

сводка четырёх операций
a = {1, 2, 3}
b = {3, 4}

print(sorted(a | b))
print(sorted(a & b))
print(sorted(a - b))
print(sorted(a ^ b))
Вывод
[1, 2, 3, 4]
[3]
[1, 2]
[1, 2, 4]

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

ЗаписьМетодЧто делает
a | ba.union(b)все элементы обоих множеств
a & ba.intersection(b)только общие элементы
a - ba.difference(b)элементы a без элементов b — порядок важен
a ^ ba.symmetric_difference(b)элементы ровно одного из двух
a -= ba.difference_update(b)вычесть b из a на месте
a ^= ba.symmetric_difference_update(b)оставить уникальное на месте

Что дальше

Разность отвечает на вопрос «чего не хватает» — и это готовый инструмент для следующей темы. В уроке 9 проверяем «вкладывается ли один комплект в другой»: issubset и issuperset вернут True или False одной строкой — а разность там же покажет, чего именно не хватает.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

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

print(sorted(a - b))
print(sorted(b - a))
print(len(a ^ b))
languages = {'py', 'js', 'go'}
known = {'py', 'go', 'rust'}

languages -= known
print(sorted(languages))
x = {'a', 'b'}
y = {'b', 'c'}

result = x ^ y
print(sorted(result))
print(len(x))
Проверь себя
0 / 7

1. Что вернёт {'а', 'б', 'в'} - {'б'}?

2. Когда a - b совпадает с b - a?

3. Что соберёт a ^ b?

4. Что случится при вычислении stock - order, если order — список?

5. Чем difference_update отличается от difference?

6. Верно ли, что a ^ b равно b ^ a?

7. Выполнено cart -= sold_out. Что произошло с cart?

Карточки терминов
Запомнено: 0 / 8
Практика

Даны множества all_files = {'report.txt', 'photo.jpg', 'notes.txt', 'data.csv'} и backed_up = {'photo.jpg', 'data.csv'}. Выведите два списка: файлы без резервной копии (разность) и файлы, которые есть только в одном из двух (симметричная разность) — оба через sorted.

practice.py
Вопросы и ответы по уроку

Чем разность множеств отличается от remove и discard?

remove и discard удаляют один названный элемент, разность вычитает целую коллекцию и возвращает новое множество, не трогая исходники. Если нужно вычесть на месте — difference_update или оператор -=.

Урок 4: remove, discard и pop

Почему a - b и b - a дают разное?

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

Что такое симметричная разность и когда она нужна?

a ^ b собирает элементы, которые есть ровно в одном из двух множеств: расхождения между двумя списками, товары с разной ценой в двух магазинах. Метод — symmetric_difference; порядок операндов не важен.

Как вычесть список из множества?

Методом difference: он принимает любую коллекцию. Оператор - со списком упадёт с TypeError: unsupported operand type(s) for -. Осторожно со строками: метод разберёт их на отдельные буквы.

Почему a - b ничего не изменил в моём множестве?

Разность возвращает новое множество и исходное не трогает. Сохраните результат в переменную: diff = a - b — или вычтите на месте: a -= b.

Понравился урок? Сошлитесь на него

«Разность несимметрична: из первого множества вычитается второе, поэтому a - b и b - a — два разных результата.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.