Язык программирования Python

Генераторы Python: Что это такое и какие проблемы они решают

Представьте себе функцию, которая возвращает все целые числа от 0 до max_number:

def numbers_up_to(max_number): output = [] for number in range(max_number + 1): output.append(number) return output
Code language: JavaScript (javascript)

В таком виде эта функция создает список [0, 1, 2, 3, 4, …], пока не достигнет max_number. При этом в памяти уже выделяется место, куда сохраняется список и его содержимое. Используя для этого аппаратные ресурсы. Проблемы нет если max_number мал…

… но попробуйте использовать эту функцию с numbers_up_to(623 * 10 ** 21). Нет, не пытайтесь. Ваш компьютер сойдет с ума. Я серьезно.

Для этого у нас есть более эффективная альтернатива: генераторы! Давайте превратим эту функцию в генератор.

Это просто: мы не создаем никакого списка и используем yield вместо return:

def numbers_up_to(max_number): for number in range(max_number + 1): yield number
Code language: JavaScript (javascript)

Теперь попробуйте использовать эту функцию с гигантским числом: numbers_up_to_as(623 * 10 ** 21). На этот раз вы можете попробовать. Может быть, ваш компьютер даже не сломается.

Он будет вычислять первый элемент последовательности только тогда, когда ему это нужно. В результате вы получите 1 и остановку. Он больше ничего не обрабатывает, ничего не выделяет в памяти. Ничего. Пока вы не попросите следующий номер. Затем он забывает о первом и дает вам второй. И так далее. Вы просите, и он дает вам по очереди: третий, потом четвертый, потом пятый и так далее. По очереди.

Вместо того чтобы создавать весь список, он создает генератор и вычисляет один за другим элементы, в зависимости от необходимости доступа к ним, и фактически он будет вычислять что-то только при каждом next() – это функция, которая вызывается внутри, если вы передаете генератор, например, в for.

Но next() можно использовать и вручную – что очень удобно для изучения:

my_first_generator = numbers_up_to(42) next(my_first_generator) next(my_first_generator) next(my_first_generator) next(my_first_generator) next(my_first_generator)

В моих примерах даже диапазон, который является родным для Python 3, уже сам по себе является генератором.

Генераторы очень полезны и очень бережно относятся к памяти. Но, конечно, есть некоторые ограничения: например, вы не можете использовать два for в одном генераторе напрямую – генераторы только продвигаются по последовательности, они никогда не возвращаются в ее начало. Поэтому, когда первый for исчерпает генератор, второй for больше не сможет его использовать.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *