Осваиваем генераторы в Python: улучшение производительности с помощью yield
Генераторы в Python – это особые функции, которые позволяют возвращать последовательности значений по мере их запроса, не загружая всю информацию в память сразу. Это особенно важно при работе с большими данными, такими как строки, файлы или базы данных, генератор списков python где хранение всех значений одновременно может стать слишком затратным. Функции создаются с использованием ключевого слова yield, которое приостанавливает выполнение функции и возвращает одно значение.
Передача исключений в генератор
Еще одним преимуществом является чистота и читаемость кода. Функции позволяют избежать сложных циклов и промежуточных переменных, упрощая логику программы. Они позволяют писать лаконичный и понятный код, который легко поддерживать и тестировать. Генераторное выражение это упрощенный с точки зрения синтаксиса способ создать генератор, не определяя и не вызывая функцию. Такой подход удобно использовать для генерации коллекций и Как стать frontend программистом с нуля их несложных преобразований.
Чтобы по окончании итерации функция next() не возвращала ошибку StopIteration, мы можем передать в нее второй аргумент:
Получить значение из генератора можно вызвав функцию next и передав в нее генератор. Функция next вызывает метод __next__ https://deveducation.com/ у переданного в нее объекта. То есть вызов next(gen) и gen.__next__() равнозначны и дают один и тот же результат. Генераторы можно использовать с разными языковыми конструкциями, которые дают возможность перебирать элементы итерируемого объекта — например, с помощью цикла for.
Пример практического применения
Основной плюс генераторов заключается в очень низком потреблении ресурсов. Благодаря этому их часто используют для расчета больших наборов результатов, где выделение памяти для одновременного хранения всех результатов нецелесообразно. Генераторы по своей сути являются теми же итераторами, только с их помощью итерировать объект можно всего один раз. Это связано с тем, что они не хранят полученные значения в памяти, а генерируют элементы «на лету». Дальше мы можем создать объект коллекции, а затем обойти все его элементы с использованием итератора. Создадим сам генератор и обернем его созданным нами декоратором.
В языке программирования Python итерируемые объекты представлены классом collections.abc.Iterator:
Этот контекстный менеджер принимает генераторную функцию в качестве параметра. При инициализации контекстного менеджера эта функция вызывается и создается генератор. В методе __enter__ происходит вызов функции next и генератор продвигается до первого yield возвращает значение и передает управление в вызвавший его код.
Создадим декоратор, который принимает генераторную функцию и оборачивает ее в созданный нами контекстный менеджер. Чтобы передать исключение генератор должен быть инициализирован вызовом next или send(None). Проверим тип генераторной функции и генератора используя type и функции isgeneratorfunction, isgenerator из модуля inspect.
Если мы попытаемся отправить в неинициализированный генератор значение отличное от None, то получим исключение TypeError. Обратите внимание в выводе нет никакого исключения GeneratorExit. А все потому, что оно выбрасывается в “тихом” режиме и не поднимается в вызывающий код. Но мы можем убедиться, что оно действительно было выброшено, добавив в генератор блок try except.
- Рассмотрим использование генератора для создания контекстного менеджера.
- При этом TypeError вызывается только в том случае, когда в объекте не реализован ни один из этих методов.
- Когда в генераторе больше нет значений или генератор встречает return, то выбрасывается исключение StopIteration.
- По сути, итерируемыми объектами являтся все объекты, от которых встроенная функция iter() может получить оператор.
С помощью ключевого слова yield from можно делегировать работу другому генератору. Функция main является генераторной функцией, так как в ней присутствует выражениеyield from. При вызове next, send или throw функция main делегирует работу сначала генератору gen и только после того как генератор gen завершит свою работу и отдаст все значения, начинает выполняться генератор gen_2. Контекстный менеджер должен поддерживать два метода __enter__ и __exit__. При входе в блок with вызывается метод __enter__, а при выходе из него вызывается метод __exit__. Напишем собственный контекстный менеджер GeneratorContextManager.
Встретив yield генератор возвращает значение, стоящее справа от yield в вызвавший его код и запоминает свою позицию. Если значение справа от yield отсутствует, то генератор возвращает None. Когда мы в следующий раз запросим значение из генератора, то выполнение продолжится с сохраненной позиции до следующего yield и так же вернется значение справа от yield.
Однако в подавляющем большинстве случаев они создаются как отдельные функции но, при этом, возвращают значение не через традиционный return, а с помощью ключевого слова yield. Фактически, он представляет собой объект, который является результатом вызова метода __iter__ итерируемого объекта. Его основная задача заключается в отслеживании следующего элемента в последовательности. Другими словами, итератор «знает» какой элемент в последовательности будет следующим, и может обрабатывать такие элементы по одному. Генераторы можно использовать не только для создания итерируемых объектов.
Это позволяет работать с файлами, которые могут быть слишком большими для стандартных методов обработки, не перегружая память. Python – это мощный инструмент для разработки, и одним из его секретов производительности являются генераторы. С помощью ключевого слова yield можно не только упростить код, но и улучшить его эффективность. В этой статье мы подробно разберемся, что такое генераторы, как они работают, и почему они так полезны, особенно при обработке больших объемов данных. Также стоит добавить, что встроенную функцию iter() можно вызывать с двумя аргументами, что позволит создать итератор из вызываемого объекта. В таком случае первый аргумент является вызываемым объектом, а второй выступает в роли ограничителя.
Важно отметить, что это могут быть не только те объекты, которые реализуют метод __iter__. Дело в том, что для получения итератора функция iter() в первую очередь вызывает метод __iter__, а если он не реализован — проверяет наличие метода __getitem__ и уже на его основе создает итератор. При этом TypeError вызывается только в том случае, когда в объекте не реализован ни один из этих методов. У генератора есть метод close при вызове которого выбрасывается исключение GeneratorExit и генератор завершает свою работу. Если после вызова close мы попытаемся получить значение из генератора, то будет выброшено исключение StopIteration.