Включите исполнение JavaScript в браузере, чтобы запустить приложение.
24 фев 2025

Что такое yield в Python

Итераторы в Python могут хранить большие последовательности, не нагружая при этом оперативную память. Такая возможность доступна благодаря оператору yield. Как он работает и где применяется — читайте в статье GitVerse.

Что такое yield

Это оператор для возврата из функции с «заморозкой» состояния ее локальной переменной. При выполнении цикла итератор доходит до yield, возвращает значение и останавливается до следующего вызова. 

Его удобно использовать в разных сценариях, например: при проверке большого объема данных, поиске и выводе записей, соответствующих определенным критериям. С помощью yield не придется повторно проверять уже обработанные данные, так как после выполнения нужной операции программа вернется к тому месту, на котором остановилась.

Таким образом, добавление этого ключевого слова в код Python помогает экономить оперативную память и повышает производительность программы.

Как работает yield

Для понимания принципа работы оператора нужно хорошо знать базовые понятия Python, такие как «генерация» и «итерация». Поэтому начнем с основ.

При работе со списком все входящие в него элементы можно перебирать последовательно, один за другим. Этот процесс называется итерацией. 

Например, код 

list1 = [10, 11, 12]

for i in list1 : 

print(i)
py

выведет последовательность:

10

11

12

Здесь list1 — итерируемый объект. Итерируемыми могут быть не только списки (list), но и строки, файлы — всё, для чего можно использовать for. 

Итератор создается во время генерации list1 с помощью спискового включения:

list1 = [x*5 for x in range(3)]

for i in list1 :

print(i)
py

Этот код выполняет следующие шаги:

  • создание списка list1, в котором:
  • range(3) создает последовательность трех чисел от 0 до 2;
  • x*5 умножает каждое число последовательности на 5, результаты вносятся в список;
  • вывод: for проходит по каждому элементу и выполняет print.

Результат выполнения кода:

0

5

10

Таким образом, эта программа на Python сначала создает последовательность из первых трех чисел, умноженных на 5, а затем выводит каждый элемент на экран.

Удобство итерируемых объектов — в отсутствии ограничений на количество повторных считываний. Недостаток в том, что все данные хранятся в оперативной памяти. Это ведет к снижению производительности приложения, особенно при обработке больших объемов информации.

Для решения проблемы используются генераторы. Это итерируемые объекты, данные из которых возможно считывать только по одному разу. Элементы генерируются в процессе выполнения кода и не хранятся в памяти. 

Вот как можно реализовать тот же код:

generator1 = (x*5 for x in range(3))

for i in generator1:

print(i)
py

Как работает код:

  • создание generator1 по той же логике:
  • range(3) создает последовательность трех чисел от 0 до 2;
  • x*5 умножает каждое число последовательности на 5;
  • вывод: цикл for проходит по каждому элементу generator1 и выполняет print.

Результат выполнения кода:

0

5

10

Каждый раз, когда происходит итерация for, генерируется новое значение, пока не исчерпается диапазон чисел, заданный range(). Генератор не сразу вычисляет все элементы, он делает это по мере необходимости, а затем «забывает» результаты вычислений.

Оператор yield при работе с list возвращает результат — объект, которым является генератор.

Преобразуем код в нашем примере:

def create_generator():

  list1 = range(3)

  for i in list1:

    yield i*5
 

generator1 = create_generator()

print(generator1)

for i in generator1:

  print(i)
py

Здесь мы сначала определяем генератор с помощью create_generator(). Внутри этой функции создается переменная list1, которая содержит числа из диапазона range(3). Затем в цикле for для каждого элемента list1 возвращается значение i*5 с помощью оператора yield.

В строке 6 создаем экземпляр генератора. Вызов create_generator() возвращает объект, он сохраняется в переменной generator1.

Строка 7 просто выводит generator1 (сообщает, что это генератор). На экране появится информация о нем, например <generator object create_generator at 0x000001D52A90BAC0>, так как generator1 — это не список значений, а объект.

Строки 8–9 отвечают за итерацию по генератору. Цикл for проходит по каждому элементу, который возвращает генерация. Так как внутри create_generator() было определено yield i*5, при каждой итерации будет возвращаться произведение текущего элемента i на 5.

Результат выполнения кода:

<generator object create_generator at 0x7f4ffa115ff0>

0

5

10

Первая строка — вывод объекта. Остальные строки — результаты умножения каждого элемента list1 на 5.

Рассмотрим другой пример: применим ключевое слово для сохранения состояния при остановке выполнения операции.

Исходный код:

def iterator1():

  for i in range (100000000000000):

    yield i

list_1, list_2 = [], []
 

itrtr = iterator1()

for i in range(3):

  list_1.append(next(itrtr))

for i in range(3):

  list_2.append(next(itrtr))

print(list_1)

print(list_2)
py

Разберем этот код по шагам, чтобы понять, как он работает.

Строки 1–3 описывают создание iterator1(). В строке for i in range (100000000000000) функция iterator1() создает генератор, который последовательно возвращает целые числа от 0 до 99 999 999 999 999. Ключевое слово в строке 3 позволяет сделать функцию генератором. Теперь она будет не создавать все числа сразу, а генерировать элементы поочередно по мере необходимости, не храня их в памяти.

Строка 4 задает пустые списки list_1 и list_2.

В строке 6 происходит создание экземпляра генератора:

itrtr = iterator1()

Переменная itrtr становится экземпляром генератора, созданного функцией iterator1().

Далее происходит заполнение списков.

Строки 7–8 наполняют list_1:

for i in range(3):

    list_1.append(next(itrtr))
py

В цикле трижды вызывается метод next(), который вызывает следующий сгенерированный элемент. Это приводит к добавлению первых трех значений, полученных при генерации itrtr, в список list_1.

Аналогичным образом наполняется второй пустой список list_2 в строках 9–10:

for i in range(3):

    list_2.append(next(itrtr))
py

Цикл добавляет следующие три элемента, сгенерированные тем же itrtr, в список list_2.

Две последние строки кода выводят результаты с помощью команд print(list_1) и print(list_2).

На этом этапе оба списка содержат по три элемента, которые были получены путем последовательной генерации. Поэтому вывод выглядит так:

[0, 1, 2]

[3, 4, 5]

Рассмотренный код на Python последовательно генерирует большое количество чисел, но вместо того, чтобы хранить все эти числа в памяти, он постепенно извлекает их по мере необходимости. Это особенно полезно для обработки больших наборов данных, где хранение всех элементов в памяти невозможно или неэффективно.

Пример использования в Python

Разберем применение оператора на примере программы, которая выводит числа от 2 до 20 в третьей степени.

def nextCube():

    acc = 2

    while True:

        yield acc**3  
              
        acc += 1

count = 1

for num in nextCube():

    if count > 19:

        break   

    print(num)

    count += 1
py

На выходе получаем список из 19 чисел, который состоит из чисел от 2 до 20 в третьей степени:

8

27

64

125

216

343

512

729

1000

1331

1728

2197

2744

3375

4096

4913

5832

6859

8000

Поясним логику выполнения кода.

Функция nextCube генерирует бесконечную последовательность чисел в кубе начиная с числа 2:

  • строка 2: переменная acc инициализируется со значением 2;
  • строка 3: начинается бесконечный цикл while True, который никогда не завершится естественным путем;
  • строка 4: с использованием оператора yield функция возвращает куб текущего значения переменной acc (acc**3). После этого выполнение nextCube временно останавливается, и управление передается обратно в вызов;
  • строка 5: переменная acc увеличивается на 1.

На ключевом слове функция возвращает следующее значение и останавливается на текущей итерации. После повторного обращения к программе выполнение кода начнется не заново, а с места остановки.

Строки 6–11 запрашивают и выводят сгенерированные 19 чисел:

  • строка 6: переменная count инициализируется с 1;
  • строка 7: начинается цикл for, который перебирает элементы, возвращаемые nextCube() генератором;
  • строка 8: проверяется условие выхода из for; если count больше 19, выполняется break, и цикл завершается;
  • строка 10: текущее значение куба, полученное от генератора, выводится на печать;
  • строка 11: переменная count увеличивается на единицу после каждой итерации.

Таким образом, программа эффективно создает и выводит на экран результаты расчетов с помощью механизма последовательной генерации.

Преимущества и недостатки yield

Использование ключевого слова позволяет разработчикам больше не думать о выделении и освобождении памяти. Оператор позволяет реализовать обработку больших объемов данных без потери производительности.

Однако yield делает код более сложным, из-за чего возрастает риск ошибок, увеличивается время на продумывание логики и отладку ПО. С правильным внедрением оператора в код на Python помогают AI-ассистенты — например, GigaCode, доступный на платформе GitVerse.