Summary-урок сводит вместе две линии: множества (урок 32) и словари (урок 34). Примеры идут от повторения каждой структуры по отдельности к задачам, где set и dict используются вместе — это и есть цель повторительного занятия. Весь вывод в комментариях получен запуском кода.
Пример 1. Повторение: set убирает дубликаты
# unique_chars.py
text = "hello world"
unique_chars = set(text)
unique_chars.discard(" ")
print("Уникальные символы:", sorted(unique_chars))
# Уникальные символы: ['d', 'e', 'h', 'l', 'o', 'r', 'w']
Что происходит: set(text) раскладывает строку на отдельные символы и оставляет только уникальные; discard(" ") убирает пробел, не вызывая ошибку, даже если бы пробела не было. sorted() нужен, потому что порядок в множестве не гарантирован — печатать несортированный set для проверки результата ненадёжно.
Пример 2. Повторение: четыре операции над множествами
# set_operations.py
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5}
print(set1 | set2) # {1, 2, 3, 4, 5} — объединение
print(set1 & set2) # {3, 4} — пересечение
print(set1 - set2) # {1, 2} — разность
print(set1 ^ set2) # {1, 2, 5} — симметрическая разность
print(set2 <= set1) # False — set2 не подмножество set1
print({3, 4}.issubset(set1)) # True
Что происходит: все четыре оператора возвращают новое множество, ни один не меняет set1 или set2. set2 <= set1 — False, потому что в set2 есть 5, которого нет в set1; а {3, 4} — уже настоящее подмножество.
Пример 3. Повторение: базовые операции словаря
# dict_basics.py
user = {"name": "Alice", "age": 30}
if "name" in user:
print(user["name"]) # Alice
user.update({"age": 31, "city": "New York"})
print(user) # {'name': 'Alice', 'age': 31, 'city': 'New York'}
Что происходит: in для словаря проверяет наличие ключа — здесь "name", а не значение "Alice". update() одним вызовом обновила существующий ключ age и добавила новый city.
Пример 4. Общие и уникальные ключи двух словарей через set
Здесь линии множеств и словарей впервые встречаются: ключи словаря можно превратить в set и применить к ним операции множеств.
# common_and_unique_keys.py
dict1 = {"a": 1, "b": 2, "c": 3}
dict2 = {"b": 5, "d": 7, "a": 8}
common_keys = sorted(set(dict1) & set(dict2))
print("Общие ключи:", common_keys if common_keys else "-")
# Общие ключи: ['a', 'b']
only_in_dict1 = sorted(set(dict1) - set(dict2))
print("Только в dict1:", only_in_dict1)
# Только в dict1: ['c']
Что происходит: set(dict1) перебирает словарь как обычный for и превращает ключи в множество — значения при этом теряются, они здесь не нужны. Дальше это обычные операции множеств из примера 2: & для общих ключей, - для тех, что есть только в одном словаре.
Пример 5. Проверка «подмножества» словаря по парам
# dict_subset_check.py
d1 = {"a": 1, "b": 2}
d2 = {"a": 1, "b": 2, "c": 3}
print(all(item in d2.items() for item in d1.items())) # True
d3 = {"a": 1, "b": 99}
print(all(item in d2.items() for item in d3.items())) # False
Что происходит: проверяется совпадение целиком — и ключ, и значение. У d3 ключ "b" есть в d2, но значения 99 и 2 разные, поэтому пара ("b", 99) не найдена среди пар d2.items(), и результат False.
Пример 6. Группировка по frozenset — множество как ключ словаря
Точка пересечения двух тем: frozenset (неизменяемое множество) используется как ключ словаря, потому что обычный set для этого не подходит — он нехешируем.
# group_by_frozenset.py
users = {
"alice": ["read", "write"],
"bob": ["write", "read"],
"charlie": ["read"],
}
groups = {}
for user, permissions in users.items():
key = frozenset(permissions)
groups.setdefault(key, []).append(user)
print(groups)
# {frozenset({'read', 'write'}): ['alice', 'bob'], frozenset({'read'}): ['charlie']}
# — порядок элементов внутри frozenset не гарантирован, у вас может быть другим
Что происходит: у alice и bob одинаковый набор прав, но в разном порядке в списке — как множества frozenset(["read", "write"]) и frozenset(["write", "read"]) равны, поэтому оба попадают в одну группу (порядок печати элементов внутри самого frozenset при этом не фиксирован). Подробный разбор frozenset — в уроке 34.
Пример 7. Ловушка: {} — это словарь, а не множество
# empty_literal_trap.py
empty_dict = {}
empty_set = set()
print(type(empty_dict), type(empty_set))
# <class 'dict'> <class 'set'>
d = {"x": 1}
d.update({"x": 2}) # тот же ключ — не дубликат, а замена
print(d) # {'x': 2}
print(len(d)) # 1 — ключ остался единственным
Что происходит: синтаксис {} в Python исторически закреплён за словарём — для пустого множества нужен вызов set(). Второй частый миф: update() с уже существующим ключом не создаёт вторую запись, а перезаписывает значение первой.
Пример 8. in означает разное для set и для dict
# in_set_vs_dict.py
s = {"a", "b", "c"}
print("a" in s) # True — проверка элемента
d = {"a": 1, "b": 2}
print("a" in d) # True — проверка ключа
print(1 in d) # False — 1 не ключ, а значение
print(1 in d.values()) # True — а вот так проверяется значение
Что происходит: для set оператор in ищет элемент; для dict — по умолчанию только ключ. Чтобы проверить значение словаря, нужно явно указать .values() — без этого 1 in d даёт неожиданный False, хотя значение 1 в словаре есть.
Пример 9. Собираем вместе: инвентаризация по двум складам
Финальная задача объединяет обе темы: множества находят полный список товаров, а словарь через dict comprehension считает, на скольких складах есть каждый товар.
# warehouse_inventory.py
warehouse_a = {"bolt", "nut", "screw"}
warehouse_b = {"nut", "washer"}
all_items = warehouse_a | warehouse_b
counts = {item: (item in warehouse_a) + (item in warehouse_b) for item in all_items}
print(sorted(all_items))
# ['bolt', 'nut', 'screw', 'washer']
print(dict(sorted(counts.items())))
# {'bolt': 1, 'nut': 2, 'screw': 1, 'washer': 1}
Что происходит: (item in warehouse_a) + (item in warehouse_b) складывает два bool — в Python True равно 1, False равно 0, поэтому сумма и есть количество складов с этим товаром. "nut" есть на обоих складах — счётчик 2, остальные товары — только на одном.
Что делать дальше
Если какой-то пример вызвал вопрос — вернитесь к уроку 32 про множества или уроку 34 про словари, затем переходите к заданиям этого summary-урока.