💻 Примеры

⚡ set и dict рядом — что общее, что разное

set1, set2 = {1, 2, 3}, {2, 3, 4}
print(set1 & set2)                    # {2, 3}       — общие элементы
d1, d2 = {"a": 1}, {"a": 1, "b": 2}
print(set(d1) & set(d2))              # {'a'}        — общие ключи
print("a" in d1, 1 in d1.values())    # True True    — in по ключу / по значению
empty_dict, empty_set = {}, set()     # {} — это dict!
setdict
Хранитуникальные элементыпары ключ-значение
Пустой литералset(){}
in проверяетэлементключ (не значение)
Общие элементы/ключиset1 & set2set(d1) & set(d2)
Топ-3 ошибки: ожидать от set сохранения порядка вставки · путать элемент множества и ключ словаря · считать, что {} создаёт пустое множество, а не пустой словарь.

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-урока.