← Назад к вопросам

Зачем нужен Set?

1.0 Junior🔥 101 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Зачем нужен Set в Python

Set — это одна из самых недооценённых структур данных в Python. Её часто путают с List и Dict, но она решает совершенно специфические задачи. Разберусь подробно.

Основная идея: уникальность и скорость

Set — это неупорядоченная коллекция уникальных элементов. В отличие от List, Set:

  • Не хранит дубликаты: если добавить один элемент дважды, он останется в одной копии
  • Очень быстро ищет элементы: O(1) вместо O(n) как в List
  • Использует хеширование: элементы должны быть hashable (неизменяемые)
# List: медленная проверка наличия элемента
my_list = [1, 2, 3, 4, 5]
if 3 in my_list:  # O(n) — проверяет каждый элемент
    print("Found")

# Set: быстрая проверка
my_set = {1, 2, 3, 4, 5}
if 3 in my_set:  # O(1) — прямой доступ
    print("Found")

Зачем нужен Set: основные применения

1. Удаление дубликатов

Это самая частая задача:

# Задача: удалить дубликаты из списка
numbers = [1, 2, 3, 2, 4, 1, 5, 3]

# Способ 1: через Set (самый быстрый)
unique_numbers = list(set(numbers))
print(unique_numbers)  # [1, 2, 3, 4, 5]

# Способ 2: через List comprehension (медленнее, но сохраняет порядок)
seen = set()
unique_numbers = [x for x in numbers if not (x in seen or seen.add(x))]

# Способ 3: dict.fromkeys() (Python 3.7+, сохраняет порядок)
unique_numbers = list(dict.fromkeys(numbers))

Обработка миллионов элементов:

import time

numbers = list(range(1000000)) * 10  # 10 миллионов элементов

# Set: очень быстро
start = time.time()
unique = set(numbers)
print(f"Set: {time.time() - start:.4f}s")  # ~0.05s

# List с проверкой: медленно
start = time.time()
unique = []
for num in numbers:
    if num not in unique:  # O(n) проверка!
        unique.append(num)
print(f"List: {time.time() - start:.4f}s")  # ~60s

2. Проверка наличия элемента

Eсли часто проверяешь наличие элемента — используй Set:

# Задача: проверить, есть ли в списке запрещённые слова
banned_words = ['spam', 'abuse', 'hate']

# МЕДЛЕННО: O(n) для каждой проверки
text = "This is a message".split()
for word in text:
    if word.lower() in banned_words:  # O(n)
        print(f"Banned: {word}")

# БЫСТРО: O(1) для каждой проверки
banned_words_set = {'spam', 'abuse', 'hate'}
for word in text:
    if word.lower() in banned_words_set:  # O(1)
        print(f"Banned: {word}")

Для большого количества проверок разница критична:

# 10 миллионов проверок
banned = set(open('banned_words.txt').read().split())
text_tokens = [...]  # 10 млн слов

for token in text_tokens:
    if token in banned:  # O(1) — мгновенно
        # обработка

3. Операции с множествами

Set поддерживает математические операции над множествами:

set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}

# Пересечение (AND): элементы, которые есть в обоих
intersection = set_a & set_b
print(intersection)  # {3, 4}

# Объединение (OR): все уникальные элементы
union = set_a | set_b
print(union)  # {1, 2, 3, 4, 5, 6}

# Разность (MINUS): элементы только в первом
difference = set_a - set_b
print(difference)  # {1, 2}

# Симметричная разность (XOR): элементы в одном, но не в обоих
sym_diff = set_a ^ set_b
print(sym_diff)  # {1, 2, 5, 6}

# Проверка подмножества
small = {1, 2}
if small <= set_a:  # True
    print("small is subset of set_a")

Практический пример: найти общих друзей

alice_friends = {'Bob', 'Charlie', 'David', 'Eve'}
bob_friends = {'Alice', 'Charlie', 'Frank', 'Grace'}

# Кто есть в друзьях у обоих?
common_friends = alice_friends & bob_friends
print(common_friends)  # {'Charlie'}

# Кого знает Alice, но не Bob?
only_alice = alice_friends - bob_friends
print(only_alice)  # {'Bob', 'David', 'Eve'}

4. Дедупликация в потоке данных

# Задача: найти все уникальные IP адреса в логах
with open('access.log') as f:
    unique_ips = set()
    for line in f:
        ip = line.split()[0]
        unique_ips.add(ip)

print(f"Total unique IPs: {len(unique_ips)}")

# Это очень быстро, даже для файлов в гигабайты
# Потому что Set хранит только хеш (8-16 байт), не сам IP

5. Отслеживание состояния

# Задача: отслеживать, какие пользователи онлайн
class ChatRoom:
    def __init__(self):
        self.online_users = set()  # Быстрая проверка: есть ли пользователь онлайн?
    
    def user_joined(self, user_id):
        self.online_users.add(user_id)
    
    def user_left(self, user_id):
        self.online_users.discard(user_id)  # remove() вызывает ошибку, если нет
    
    def is_online(self, user_id):
        return user_id in self.online_users  # O(1)
    
    def get_online_count(self):
        return len(self.online_users)

Set vs List vs Dict: когда что использовать

# ИСПОЛЬЗУЙ LIST ЕСЛИ:
# - Нужен порядок элементов
# - Нужен доступ по индексу
# - Допускаются дубликаты
scores = [100, 95, 87, 92]  # Порядок важен
first = scores[0]  # Доступ по индексу

# ИСПОЛЬЗУЙ SET ЕСЛИ:
# - Нужна уникальность
# - Часто проверяешь наличие элемента
# - Нужны операции над множествами
visited = {1, 2, 3}  # Быстрая проверка 'if x in visited'
unique = set([1, 2, 2, 3])  # Автоматически удаляет дубликаты

# ИСПОЛЬЗУЙ DICT ЕСЛИ:
# - Нужна связь ключ-значение
# - Нужен быстрый поиск по ключу
user_ages = {'Alice': 30, 'Bob': 25}  # Ключ → значение

Важные характеристики Set

# Set содержит только hashable элементы
my_set = {1, 'hello', (1, 2)}  # Можно: int, str, tuple

# ОШИБКА: list не hashable
my_set = {1, [2, 3]}  # TypeError!

# ОШИБКА: dict не hashable
my_set = {1, {'key': 'value'}}  # TypeError!

# Но frozenset (неизменяемый set) — hashable
my_set = {1, frozenset([2, 3])}  # Ок!

# Set сам является изменяемым, поэтому не может быть ключом dict
my_dict = {(1, 2): 'value'}  # Ок! Tuple hashable
my_dict = {{1, 2}: 'value'}  # TypeError!
my_dict = {frozenset([1, 2]): 'value'}  # Ок!

Производительность

import timeit

# Проверка наличия элемента в 1 миллион элементов
my_list = list(range(1000000))
my_set = set(range(1000000))

list_time = timeit.timeit(lambda: 999999 in my_list, number=100)
set_time = timeit.timeit(lambda: 999999 in my_set, number=100)

print(f"List: {list_time:.4f}s")  # ~0.1s
print(f"Set: {set_time:.4f}s")   # ~0.001s (в 100 раз быстрее!)

Выводы

Set нужен для:

  1. Удаления дубликатов — самый быстрый способ
  2. Быстрого поиска элементов — O(1) вместо O(n)
  3. Операций над множествами — пересечение, объединение, разность
  4. Отслеживания уникальности — какие ID уже видели
  5. Работы с большими данными — Set экономит память и время

Золотое правило: если часто проверяешь наличие элемента или нужна уникальность — используй Set, не List!

Зачем нужен Set? | PrepBro