Команда Python for Devs подготовила перевод статьи о генераторах множеств в Python. С их помощью можно создавать, преобразовывать и фильтровать множества одной строкой кода. Разбираем примеры, практические приёмы и ошибки, которых стоит избегать.
Генераторы множеств в Python позволяют лаконично создавать и обрабатывать множества в коде. Они формируют множества с чистым синтаксисом, делая код более читаемым и «питонячним». С их помощью можно создавать, преобразовывать и фильтровать множества — полезный навык, который стоит добавить в свой арсенал Python-разработчика.
К концу этого урока вы поймёте, что:
В Python есть генераторы множеств, которые позволяют создавать множества с компактным синтаксисом.
Python поддерживает четыре вида генераторов: для списков, множеств, словарей и генераторные выражения.
Генератор множества записывается так:
{выражение for элемент in итерируемый_объект [if условие]}
.Множества не могут содержать дубликатов — они гарантируют уникальность всех элементов.
Чтобы получить максимальную пользу от этого урока, полезно быть знакомым с базовыми концепциями Python: циклами
for
, итерируемыми объектами, генераторами списков и словарей.
Создание и преобразование множеств в Python
В процессе работы на Python часто возникает необходимость создавать, наполнять и преобразовывать множества. Для этого можно использовать литералы множеств, конструктор set()
и циклы for
. В следующих разделах мы рассмотрим, как работать с этими инструментами. Вы также познакомитесь с генераторами множеств — мощным способом обработки данных в Python.
Создание множеств с помощью литералов и set()
Чтобы создать новое множество, можно использовать литералы. Литерал множества — это набор элементов, заключённых в фигурные скобки. Синтаксис выглядит так:
{element_1, element_2, ..., element_N}
Элементы множества должны быть хэшируемыми объектами. Внутри литерала объекты могут повторяться, но в итоговом множестве сохранится только один экземпляр каждого — множества не допускают дубликатов. Вот простой пример:
>>> colors = {"blue", "red", "green", "orange", "green"}
>>> colors
{'red', 'green', 'orange', 'blue'}
>>> colors.add("purple")
>>> colors
{'red', 'green', 'orange', 'purple', 'blue'}
В этом примере создаётся множество с названиями цветов. Все элементы в нём уникальны. Новые значения можно добавлять с помощью метода .add()
. Помните, что множества — это неупорядоченные коллекции, поэтому порядок элементов в них обычно не совпадает с порядком вставки.
Создавать множества можно и через конструктор set()
на основе итерируемого объекта:
>>> numbers = [2, 2, 1, 4, 2, 3]
>>> set(numbers)
{1, 2, 3, 4}
Здесь множество формируется из списка чисел. Обратите внимание, что в итоговом множестве отсутствуют повторяющиеся значения. На практике конструктор set()
отлично подходит для удаления дубликатов из любых итерируемых объектов.
Чтобы создать пустое множество, используйте конструктор set()
без аргументов:
>>> set()
set()
Создать пустое множество через литерал невозможно, так как пара фигурных скобок {}
в Python обозначает пустой словарь, а не множество. Поэтому для пустого множества всегда нужно вызывать set()
.
Использование циклов for для наполнения множеств
Иногда требуется начать с пустого множества и динамически добавлять в него элементы. Для этого можно воспользоваться циклом for
. Допустим, вы хотите собрать множество уникальных слов из текста. Сделать это можно так:
>>> unique_words = set()
>>> text = """
... Beautiful is better than ugly
... Explicit is better than implicit
... Simple is better than complex
... Complex is better than complicated
... """.lower()
>>> for word in text.split():
... unique_words.add(word)
...
>>> unique_words
{
'beautiful',
'ugly',
'better',
'implicit',
'complicated',
'than',
'explicit',
'is',
'complex',
'simple'
}
В этом примере сначала создаётся пустое множество с помощью set()
. Затем весь текст приводится к нижнему регистру методом .lower()
, чтобы сделать разбор слов независимым от регистра. После этого цикл перебирает список слов, полученный разбиением текста.
Для разбиения используется метод .split()
, который разделяет строку на слова по пробелам.
На каждой итерации цикла в множество добавляется очередное слово. Если оно уже есть в множестве, новый экземпляр не сохраняется. Именно поэтому, например, слово better
встречается в итоговом множестве только один раз. Такой цикл выглядит просто и наглядно, но тот же результат можно получить ещё более лаконично — с помощью генератора множества.
Знакомство с генераторами множеств
Генераторы множеств позволяют создавать множества в одну строку с помощью цикла for
. Если вы уже работали с генераторами списков, то освоить генераторы множеств будет легко — синтаксис у них схожий. Основное отличие в том, что генераторы множеств используют фигурные скобки вместо квадратных.
Общий синтаксис выглядит так:
{выражение for элемент in итерируемый_объект [if условие]}
Генератор множества возвращает новое множество. Элементы для него формируются из элементов входного итерируемого объекта. В конце можно указать дополнительное условие, чтобы фильтровать коллекцию или генерировать элементы выборочно.
Синтаксис генератора множества включает четыре ключевых элемента:
Фигурные скобки
{}
— обозначают генератор множества.Выражение генератора — формирует элемент на каждой итерации.
Текущий элемент — имя переменной, принимающей значение из итерируемого объекта.
Итерируемый объект — это может быть список, кортеж, множество, генератор или другой итерируемый тип.
Пример: создаём множество уникальных слов из текста с помощью генератора:
>>> text = """
... Beautiful is better than ugly
... Explicit is better than implicit
... Simple is better than complex
... Complex is better than complicated
... """.lower()
>>> {word for word in text.split()}
{
'beautiful',
'ugly',
'better',
'implicit',
'complicated',
'than',
'explicit',
'is',
'complex',
'simple'
}
Здесь генератор множества извлекает уникальные слова из исходного текста. Код получается короче и более читаемым.
На практике, если данные не требуют преобразования, цикл или генератор множества можно заменить ещё более простым вариантом:
>>> set(text.split())
{
'beautiful',
'ugly',
'better',
'implicit',
'complicated',
'than',
'explicit',
'is',
'complex',
'simple'
}
Здесь результат тот же, что и с циклом или генератором, потому что элементы множества не нуждаются в дополнительной обработке.
Генераторы могут содержать несколько операторов for
. В этом случае первый for
(слева) перебирает внешнюю коллекцию, следующий — вложенный уровень и так далее.
Например, у нас есть список списков. Каждый вложенный список содержит числа, и мы хотим создать множество их квадратов. Для этого можно написать генератор с двумя for
:
>>> matrix = [
... [9, 3, 8, 3],
... [4, 5, 2, 8],
... [6, 4, 3, 1],
... [1, 0, 4, 5],
... ]
>>> {value**2 for row in matrix for value in row}
{64, 1, 0, 4, 36, 9, 16, 81, 25}
В этом примере первый цикл перебирает строки матрицы, второй — числа внутри каждой строки. В итоге получается множество квадратов.
Важно отметить: хотя в матрице 16 элементов, итоговое множество содержит только 9 значений, потому что множество хранит только уникальные элементы. Дубликаты автоматически отбрасываются.
Использование генераторов множеств в Python
С помощью генераторов множеств можно создавать новые множества, преобразовывать существующие и даже фильтровать элементы с помощью условий. В следующих разделах мы разберём, как применять генераторы множеств для этих задач.
Начнём с создания новых множеств на основе существующих итерируемых объектов.
Создание множеств из итерируемых объектов
Иногда у вас есть итерируемый набор данных, и нужно сформировать множество, применив к элементам какую-либо трансформацию.
Например, у вас есть список инструментов и библиотек. Необходимо регулярно проверять, есть ли тот или иной инструмент в этом списке. Для этого используют проверку принадлежности (in
). Чтобы сравнение было регистронезависимым, все элементы нужно привести к нижнему регистру.
Так как множества эффективнее списков при проверке принадлежности, вы преобразуете список инструментов в множество слов в нижнем регистре:
>>> tools = ["Python", "Django", "Flask", "pandas", "NumPy"]
>>> tools_set = {tool.lower() for tool in tools}
>>> tools_set
{'django', 'numpy', 'flask', 'pandas', 'python'}
>>> "python".lower() in tools_set
True
>>> "Pandas".lower() in tools_set
True
>>> "Numpy".lower() in tools_set
True
В выделенной строке используется генератор множества, который нормализует все значения в нижний регистр, обеспечивая единообразное сравнение. Затем с помощью оператора in
выполняются проверки на вхождение и определяется, есть ли указанный инструмент в исходном списке.
Если же над элементами не требуется выполнять преобразований, то вовсе не обязательно использовать генератор множества — достаточно вызвать set(iterable)
. В приведённом примере генератор оправдан, так как названия инструментов нужно было привести к нижнему регистру.
Преобразование существующих множеств
Генераторы множеств можно использовать и для быстрого преобразования уже имеющихся множеств. Представим, что у вас есть набор адресов электронной почты, введённых пользователями. Вы заметили, что некоторые адреса содержат лишние пробелы в начале и в конце, а также заглавные буквы. Более того, среди них встречается дубликат.
Исправить ситуацию можно с помощью генератора множества:
>>> emails = {
... " alice@example.org ",
... "BOB@example.com",
... "charlie@EXAMPLE.com",
... "David@example.net",
... " bob@example.com",
... "JohnDoe@example.com",
... }
>>> {email.strip().lower() for email in emails}
{
'alice@example.org',
'bob@example.com',
'johndoe@example.com',
'charlie@example.com',
'david@example.net'
}
В выделенной строке используется генератор множества, который убирает лишние пробелы методом .strip()
, а затем приводит все адреса к нижнему регистру с помощью .lower()
. В результате очистки дубликаты исчезают, так как множества могут содержать только уникальные элементы.
Фильтрация элементов множеств
Иногда требуется отфильтровать существующее множество и создать новое — только с теми элементами, которые удовлетворяют определённому условию. Например, пусть нужно выделить из множества адресов электронной почты только те, что принадлежат домену .com
. Для этого можно использовать такой генератор множества:
>>> emails_set = {
... "alice@example.org",
... "bob@example.com",
... "johndoe@example.com",
... "charlie@example.com",
... "david@example.net",
... }
>>> {email for email in emails_set if email.endswith(".com")}
{'bob@example.com', 'charlie@example.com', 'johndoe@example.com'}
В этом примере используется условие, которое формирует новое множество только из адресов с доменом .com
.
Когда стоит использовать генераторы множеств
Выбирая между генератором множества, обычным циклом или комбинацией вызовов функций, стоит учитывать два фактора:
Лаконичность: генераторы множеств сокращают количество кода по сравнению с эквивалентными циклами
for
.Читаемость: генераторы множеств делают код более наглядным и понятным.
На практике генераторы множеств удобно использовать, когда нужно:
создавать множества из существующих итерируемых объектов с применением преобразований,
преобразовывать элементы уже существующего множества,
фильтровать элементы множества по условию.
Мы уже рассмотрели все эти операции. Но по мере практики в Python вы наверняка найдёте и другие полезные сценарии применения генераторов множеств.
Тем не менее бывают ситуации, когда их использование избыточно. Например, если нужно просто удалить дубликаты из итерируемого объекта, достаточно воспользоваться конструктором set()
:
>>> set([2, 2, 1, 4, 2, 3])
{1, 2, 3, 4}
Если при формировании множества над элементами не требуется выполнять преобразования, то лучше ограничиться вызовом set()
. Это простой и эффективный способ избавиться от повторов. Важно помнить, что элементы во входном итерируемом объекте должны быть хэшируемыми, а исходный порядок при этом не сохраняется.
Изучаем распространённые ошибки
При работе с генераторами множеств в Python стоит избегать некоторые bad practices. Наиболее частые из них:
использование слишком сложных выражений внутри генератора,
написание вложенных генераторов с несколькими
for
и условиями,попытка обращаться к переменным генератора извне,
выполнение слишком «дорогих» преобразований при формировании элементов множества.
Иногда выражение в генераторе оказывается чрезмерно сложным.
Например, вы создаёте множество из списка чисел: если число чётное — берёте его квадрат, если нечётное — куб:
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> {number**2 if number % 2 == 0 else number**3 for number in numbers}
{64, 1, 4, 36, 100, 16, 343, 729, 27, 125}
Такой генератор работает, но выглядит перегруженным и плохо читается. В подобных случаях лучше использовать обычный цикл for
:
>>> result_set = set()
>>> for number in numbers:
... if number % 2 == 0:
... value = number**2
... else:
... value = number**3
... result_set.add(value)
...
>>> result_set
{64, 1, 4, 36, 100, 16, 343, 729, 27, 125}
Результат будет тот же, но код станет понятнее и нагляднее.
Вложенные генераторы с несколькими for
и условиями тоже снижают читаемость, поэтому лучше отдавать предпочтение явным циклам.
Кроме того, важно помнить: переменные, объявленные внутри генератора, доступны только в его области видимости. Попытка обратиться к ним снаружи приведёт к ошибке:
>>> {number**3 for number in range(1, 11)}
{64, 1, 512, 8, 1000, 343, 216, 729, 27, 125}
>>> number
Traceback (most recent call last):
...
NameError: name 'number' is not defined
Переменная number
существует только внутри генератора.
Наконец, если для формирования элементов множества требуется выполнять ресурсоёмкие преобразования, стоит учитывать, что построение итогового множества может занять значительное время. В таких случаях можно рассмотреть альтернативу — генераторы (generator expressions), которые формируют элементы по мере необходимости.
Русскоязычное сообщество про Python

Друзья! Эту статью перевела команда Python for Devs — канала, где каждый день выходят самые свежие и полезные материалы о Python и его экосистеме. Подписывайтесь, чтобы ничего не пропустить!
Заключение
Вы подробно познакомились с генераторами множеств в Python. Это мощный инструмент для создания, преобразования и фильтрации множеств с помощью лаконичного и чистого синтаксиса. Вы также узнали о некоторых плохих практиках, которых стоит избегать при их использовании.
Генераторы множеств — отличный инструмент для Python-разработчиков. Они предоставляют «питоничный» и удобный способ работы с множествами.
В этом уроке вы:
научились создавать новые множества с помощью краткого синтаксиса,
преобразовывать существующие множества через генераторы,
фильтровать ненужные элементы,
разбирать, когда стоит применять генераторы множеств в коде.
С этими навыками вы сможете писать более читаемый и выразительный код для работы с множествами. Чем чаще вы будете использовать генераторы множеств, тем ценнее они станут в вашей практике.
Andrey_Solomatin
Вроде простая задача взять и описать то, что уже описанно тысячи раз, но не все справляются.
https://peps.python.org/pep-0448/